'
- content = ' '.join(lines) # To keep the whitespace between lines
+ content = ' '.join(lines) # To keep the whitespace between lines
content = shared.fixPotentiallyInvalidUTF8Data(content)
- content = unicode(content, 'utf-8)')
- textEdit.setHtml(QtCore.QString(content))
+ content = unic(ustr(content))
+ textEdit.setHtml(content)
def on_action_InboxMarkUnread(self):
tableWidget = self.getCurrentMessagelist()
@@ -2949,8 +2986,8 @@ class MyForm(settingsmixin.SMainWindow):
# modified = 0
for row in tableWidget.selectedIndexes():
currentRow = row.row()
- msgid = tableWidget.item(currentRow, 3).data()
- msgids.add(msgid)
+ msgid = as_msgid(tableWidget.item(currentRow, 3).data())
+ msgids.add(sqlite3.Binary(msgid))
# if not tableWidget.item(currentRow, 0).unread:
# modified += 1
self.updateUnreadStatus(tableWidget, currentRow, msgid, False)
@@ -2958,24 +2995,25 @@ class MyForm(settingsmixin.SMainWindow):
# for 1081
idCount = len(msgids)
# rowcount =
- sqlExecuteChunked(
+ total_row_count = sqlExecuteChunked(
'''UPDATE inbox SET read=0 WHERE msgid IN ({0}) AND read=1''',
- idCount, *msgids
+ False, idCount, *msgids
)
+ if total_row_count < 1:
+ sqlExecuteChunked(
+ '''UPDATE inbox SET read=0 WHERE msgid IN ({0}) AND read=1''',
+ True, idCount, *msgids
+ )
self.propagateUnreadCount()
- # tableWidget.selectRow(currentRow + 1)
- # This doesn't de-select the last message if you try to mark it
- # unread, but that doesn't interfere. Might not be necessary.
- # We could also select upwards, but then our problem would be
- # with the topmost message.
- # tableWidget.clearSelection() manages to mark the message
- # as read again.
# Format predefined text on message reply.
def quoted_text(self, message):
if not config.safeGetBoolean('bitmessagesettings', 'replybelow'):
- return '\n\n------------------------------------------------------\n' + message
+ return (
+ '\n\n------------------------------------------------------\n' +
+ message
+ )
quoteWrapper = textwrap.TextWrapper(
replace_whitespace=False, initial_indent='> ',
@@ -3005,7 +3043,7 @@ class MyForm(settingsmixin.SMainWindow):
self.ui.comboBoxSendFrom, self.ui.comboBoxSendFromBroadcast
):
for i in range(box.count()):
- if str(box.itemData(i).toPyObject()) == address:
+ if ustr(box.itemData(i)) == ustr(address):
box.setCurrentIndex(i)
break
else:
@@ -3039,17 +3077,23 @@ class MyForm(settingsmixin.SMainWindow):
acct = accountClass(toAddressAtCurrentInboxRow)
fromAddressAtCurrentInboxRow = tableWidget.item(
currentInboxRow, column_from).address
- msgid = tableWidget.item(currentInboxRow, 3).data()
+ msgid = as_msgid(tableWidget.item(currentInboxRow, 3).data())
queryreturn = sqlQuery(
- "SELECT message FROM inbox WHERE msgid=?", msgid
- ) or sqlQuery("SELECT message FROM sent WHERE ackdata=?", msgid)
+ "SELECT message FROM inbox WHERE msgid=?", sqlite3.Binary(msgid)
+ ) or sqlQuery("SELECT message FROM sent WHERE ackdata=?", sqlite3.Binary(msgid))
+ if len(queryreturn) < 1:
+ queryreturn = sqlQuery(
+ "SELECT message FROM inbox WHERE msgid=CAST(? AS TEXT)", msgid
+ ) or sqlQuery("SELECT message FROM sent WHERE ackdata=CAST(? AS TEXT)", msgid)
if queryreturn != []:
for row in queryreturn:
messageAtCurrentInboxRow, = row
+ messageAtCurrentInboxRow = messageAtCurrentInboxRow.decode("utf-8", "replace")
acct.parseMessage(
toAddressAtCurrentInboxRow, fromAddressAtCurrentInboxRow,
tableWidget.item(currentInboxRow, 2).subject,
- messageAtCurrentInboxRow)
+ messageAtCurrentInboxRow
+ )
widget = {
'subject': self.ui.lineEditSubject,
'from': self.ui.comboBoxSendFrom,
@@ -3062,23 +3106,26 @@ class MyForm(settingsmixin.SMainWindow):
)
# toAddressAtCurrentInboxRow = fromAddressAtCurrentInboxRow
elif not config.has_section(toAddressAtCurrentInboxRow):
- QtGui.QMessageBox.information(
- self, _translate("MainWindow", "Address is gone"),
+ QtWidgets.QMessageBox.information(
+ self,
+ _translate("MainWindow", "Address is gone"),
_translate(
"MainWindow",
- "Bitmessage cannot find your address %1. Perhaps you"
+ "Bitmessage cannot find your address {0}. Perhaps you"
" removed it?"
- ).arg(toAddressAtCurrentInboxRow), QtGui.QMessageBox.Ok)
+ ).format(toAddressAtCurrentInboxRow),
+ QtWidgets.QMessageBox.Ok)
elif not config.getboolean(
toAddressAtCurrentInboxRow, 'enabled'):
- QtGui.QMessageBox.information(
- self, _translate("MainWindow", "Address disabled"),
+ QtWidgets.QMessageBox.information(
+ self,
+ _translate("MainWindow", "Address disabled"),
_translate(
"MainWindow",
"Error: The address from which you are trying to send"
- " is disabled. You\'ll have to enable it on the"
- " \'Your Identities\' tab before using it."
- ), QtGui.QMessageBox.Ok)
+ " is disabled. You\'ll have to enable it on the \'Your"
+ " Identities\' tab before using it."
+ ), QtWidgets.QMessageBox.Ok)
else:
self.setBroadcastEnablementDependingOnWhetherThisIsAMailingListAddress(toAddressAtCurrentInboxRow)
broadcast_tab_index = self.ui.tabWidgetSend.indexOf(
@@ -3096,7 +3143,7 @@ class MyForm(settingsmixin.SMainWindow):
tableWidget.item(currentInboxRow, column_from).label or (
isinstance(acct, GatewayAccount) and
fromAddressAtCurrentInboxRow == acct.relayAddress):
- self.ui.lineEditTo.setText(str(acct.fromAddress))
+ self.ui.lineEditTo.setText(ustr(acct.fromAddress))
else:
self.ui.lineEditTo.setText(
tableWidget.item(currentInboxRow, column_from).accountString()
@@ -3111,7 +3158,7 @@ class MyForm(settingsmixin.SMainWindow):
' reply to the chan address.')
if toAddressAtCurrentInboxRow == \
tableWidget.item(currentInboxRow, column_to).label:
- self.ui.lineEditTo.setText(str(toAddressAtCurrentInboxRow))
+ self.ui.lineEditTo.setText(ustr(toAddressAtCurrentInboxRow))
else:
self.ui.lineEditTo.setText(
tableWidget.item(currentInboxRow, column_to).accountString()
@@ -3120,7 +3167,7 @@ class MyForm(settingsmixin.SMainWindow):
self.setSendFromComboBox(toAddressAtCurrentInboxRow)
quotedText = self.quoted_text(
- unicode(messageAtCurrentInboxRow, 'utf-8', 'replace'))
+ unic(ustr(messageAtCurrentInboxRow)))
widget['message'].setPlainText(quotedText)
if acct.subject[0:3] in ('Re:', 'RE:'):
widget['subject'].setText(
@@ -3156,14 +3203,15 @@ class MyForm(settingsmixin.SMainWindow):
recipientAddress = tableWidget.item(
currentInboxRow, 0).data(QtCore.Qt.UserRole)
# Let's make sure that it isn't already in the address book
- queryreturn = sqlQuery('''select * from blacklist where address=?''',
- addressAtCurrentInboxRow)
+ queryreturn = sqlQuery(
+ 'SELECT * FROM blacklist WHERE address=?',
+ dbstr(addressAtCurrentInboxRow))
if queryreturn == []:
label = "\"" + tableWidget.item(currentInboxRow, 2).subject + "\" in " + config.get(
recipientAddress, "label")
sqlExecute('''INSERT INTO blacklist VALUES (?,?, ?)''',
- label,
- addressAtCurrentInboxRow, True)
+ dbstr(label),
+ dbstr(addressAtCurrentInboxRow), True)
self.ui.blackwhitelist.rerenderBlackWhiteList()
self.updateStatusBar(_translate(
"MainWindow",
@@ -3188,15 +3236,15 @@ class MyForm(settingsmixin.SMainWindow):
messageLists = (messageLists,)
for messageList in messageLists:
if row is not None:
- inventoryHash = messageList.item(row, 3).data()
+ inventoryHash = as_msgid(messageList.item(row, 3).data())
messageList.removeRow(row)
elif inventoryHash is not None:
for i in range(messageList.rowCount() - 1, -1, -1):
- if messageList.item(i, 3).data() == inventoryHash:
+ if as_msgid(messageList.item(i, 3).data()) == inventoryHash:
messageList.removeRow(i)
elif ackData is not None:
for i in range(messageList.rowCount() - 1, -1, -1):
- if messageList.item(i, 3).data() == ackData:
+ if as_msgid(messageList.item(i, 3).data()) == ackData:
messageList.removeRow(i)
# Send item on the Inbox tab to trash
@@ -3206,8 +3254,8 @@ class MyForm(settingsmixin.SMainWindow):
return
currentRow = 0
folder = self.getCurrentFolder()
- shifted = QtGui.QApplication.queryKeyboardModifiers() \
- & QtCore.Qt.ShiftModifier
+ shifted = (QtWidgets.QApplication.queryKeyboardModifiers() &
+ QtCore.Qt.ShiftModifier)
tableWidget.setUpdatesEnabled(False)
inventoryHashesToTrash = set()
# ranges in reversed order
@@ -3216,16 +3264,21 @@ class MyForm(settingsmixin.SMainWindow):
)[::-1]:
for i in range(r.bottomRow() - r.topRow() + 1):
inventoryHashesToTrash.add(
- tableWidget.item(r.topRow() + i, 3).data())
+ sqlite3.Binary(as_msgid(tableWidget.item(r.topRow() + i, 3).data())))
currentRow = r.topRow()
self.getCurrentMessageTextedit().setText("")
tableWidget.model().removeRows(
r.topRow(), r.bottomRow() - r.topRow() + 1)
idCount = len(inventoryHashesToTrash)
- sqlExecuteChunked(
+ total_row_count = sqlExecuteChunked(
("DELETE FROM inbox" if folder == "trash" or shifted else
"UPDATE inbox SET folder='trash', read=1") +
- " WHERE msgid IN ({0})", idCount, *inventoryHashesToTrash)
+ " WHERE msgid IN ({0})", False, idCount, *inventoryHashesToTrash)
+ if total_row_count < 1:
+ sqlExecuteChunked(
+ ("DELETE FROM inbox" if folder == "trash" or shifted else
+ "UPDATE inbox SET folder='trash', read=1") +
+ " WHERE msgid IN ({0})", True, idCount, *inventoryHashesToTrash)
tableWidget.selectRow(0 if currentRow == 0 else currentRow - 1)
tableWidget.setUpdatesEnabled(True)
self.propagateUnreadCount(folder)
@@ -3244,16 +3297,20 @@ class MyForm(settingsmixin.SMainWindow):
)[::-1]:
for i in range(r.bottomRow() - r.topRow() + 1):
inventoryHashesToTrash.add(
- tableWidget.item(r.topRow() + i, 3).data())
+ sqlite3.Binary(as_msgid(tableWidget.item(r.topRow() + i, 3).data())))
currentRow = r.topRow()
self.getCurrentMessageTextedit().setText("")
tableWidget.model().removeRows(
r.topRow(), r.bottomRow() - r.topRow() + 1)
tableWidget.selectRow(0 if currentRow == 0 else currentRow - 1)
idCount = len(inventoryHashesToTrash)
- sqlExecuteChunked(
+ total_row_count = sqlExecuteChunked(
"UPDATE inbox SET folder='inbox' WHERE msgid IN({0})",
- idCount, *inventoryHashesToTrash)
+ False, idCount, *inventoryHashesToTrash)
+ if total_row_count < 1:
+ sqlExecuteChunked(
+ "UPDATE inbox SET folder='inbox' WHERE msgid IN({0})",
+ True, idCount, *inventoryHashesToTrash)
tableWidget.selectRow(0 if currentRow == 0 else currentRow - 1)
tableWidget.setUpdatesEnabled(True)
self.propagateUnreadCount()
@@ -3265,30 +3322,33 @@ class MyForm(settingsmixin.SMainWindow):
return
currentInboxRow = tableWidget.currentRow()
try:
- subjectAtCurrentInboxRow = str(tableWidget.item(
+ subjectAtCurrentInboxRow = ustr(tableWidget.item(
currentInboxRow, 2).data(QtCore.Qt.UserRole))
except:
subjectAtCurrentInboxRow = ''
# Retrieve the message data out of the SQL database
- msgid = tableWidget.item(currentInboxRow, 3).data()
+ msgid = as_msgid(tableWidget.item(currentInboxRow, 3).data())
queryreturn = sqlQuery(
- '''select message from inbox where msgid=?''', msgid)
+ 'SELECT message FROM inbox WHERE msgid=?', sqlite3.Binary(msgid))
+ if len(queryreturn) < 1:
+ queryreturn = sqlQuery(
+ 'SELECT message FROM inbox WHERE msgid=CAST(? AS TEXT)', msgid)
if queryreturn != []:
for row in queryreturn:
message, = row
+ message = message.decode("utf-8", "replace")
- defaultFilename = "".join(x for x in subjectAtCurrentInboxRow if x.isalnum()) + '.txt'
- filename = QtGui.QFileDialog.getSaveFileName(
- self,
- _translate("MainWindow","Save As..."),
- defaultFilename,
- "Text files (*.txt);;All files (*.*)")
- if filename == '':
+ defaultFilename = "".join(
+ x for x in subjectAtCurrentInboxRow if x.isalnum()) + '.txt'
+ filename = QtWidgets.QFileDialog.getSaveFileName(
+ self, _translate("MainWindow", "Save As..."), defaultFilename,
+ "Text files (*.txt);;All files (*.*)")[0]
+ if not filename:
return
try:
f = open(filename, 'w')
- f.write(message)
+ f.write(message.encode("utf-8", "replace"))
f.close()
except Exception:
logger.exception('Message not saved', exc_info=True)
@@ -3296,19 +3356,27 @@ class MyForm(settingsmixin.SMainWindow):
# Send item on the Sent tab to trash
def on_action_SentTrash(self):
+ currentRow = 0
tableWidget = self.getCurrentMessagelist()
if not tableWidget:
return
folder = self.getCurrentFolder()
- shifted = QtGui.QApplication.queryKeyboardModifiers() & QtCore.Qt.ShiftModifier
+ shifted = (QtWidgets.QApplication.queryKeyboardModifiers() &
+ QtCore.Qt.ShiftModifier)
while tableWidget.selectedIndexes() != []:
currentRow = tableWidget.selectedIndexes()[0].row()
- ackdataToTrash = tableWidget.item(currentRow, 3).data()
- sqlExecute(
+ ackdataToTrash = as_msgid(tableWidget.item(currentRow, 3).data())
+ rowcount = sqlExecute(
"DELETE FROM sent" if folder == "trash" or shifted else
"UPDATE sent SET folder='trash'"
- " WHERE ackdata = ?", ackdataToTrash
+ " WHERE ackdata = ?", sqlite3.Binary(ackdataToTrash)
)
+ if rowcount < 1:
+ sqlExecute(
+ "DELETE FROM sent" if folder == "trash" or shifted else
+ "UPDATE sent SET folder='trash'"
+ " WHERE ackdata = CAST(? AS TEXT)", ackdataToTrash
+ )
self.getCurrentMessageTextedit().setPlainText("")
tableWidget.removeRow(currentRow)
self.updateStatusBar(_translate(
@@ -3322,22 +3390,29 @@ class MyForm(settingsmixin.SMainWindow):
addressAtCurrentRow = self.ui.tableWidgetInbox.item(
currentRow, 0).data(QtCore.Qt.UserRole)
toRipe = decodeAddress(addressAtCurrentRow)[3]
- sqlExecute(
+ rowcount = sqlExecute(
'''UPDATE sent SET status='forcepow' WHERE toripe=? AND status='toodifficult' and folder='sent' ''',
- toRipe)
+ sqlite3.Binary(toRipe))
+ if rowcount < 1:
+ sqlExecute(
+ '''UPDATE sent SET status='forcepow' WHERE toripe=CAST(? AS TEXT) AND status='toodifficult' and folder='sent' ''',
+ toRipe)
queryreturn = sqlQuery('''select ackdata FROM sent WHERE status='forcepow' ''')
for row in queryreturn:
ackdata, = row
- queues.UISignalQueue.put(('updateSentItemStatusByAckdata', (
- ackdata, 'Overriding maximum-difficulty setting. Work queued.')))
+ queues.UISignalQueue.put((
+ 'updateSentItemStatusByAckdata',
+ (ackdata, 'Overriding maximum-difficulty setting.'
+ ' Work queued.')
+ ))
queues.workerQueue.put(('sendmessage', ''))
def on_action_SentClipboard(self):
currentRow = self.ui.tableWidgetInbox.currentRow()
addressAtCurrentRow = self.ui.tableWidgetInbox.item(
currentRow, 0).data(QtCore.Qt.UserRole)
- clipboard = QtGui.QApplication.clipboard()
- clipboard.setText(str(addressAtCurrentRow))
+ clipboard = QtWidgets.QApplication.clipboard()
+ clipboard.setText(ustr(addressAtCurrentRow))
# Group of functions for the Address Book dialog box
def on_action_AddressBookNew(self):
@@ -3349,7 +3424,7 @@ class MyForm(settingsmixin.SMainWindow):
0].row()
item = self.ui.tableWidgetAddressBook.item(currentRow, 0)
sqlExecute(
- 'DELETE FROM addressbook WHERE address=?', item.address)
+ 'DELETE FROM addressbook WHERE address=?', dbstr(item.address))
self.ui.tableWidgetAddressBook.removeRow(currentRow)
self.rerenderMessagelistFromLabels()
self.rerenderMessagelistToLabels()
@@ -3361,7 +3436,7 @@ class MyForm(settingsmixin.SMainWindow):
addresses_string = item.address
else:
addresses_string += ', ' + item.address
- clipboard = QtGui.QApplication.clipboard()
+ clipboard = QtWidgets.QApplication.clipboard()
clipboard.setText(addresses_string)
def on_action_AddressBookSend(self):
@@ -3371,8 +3446,7 @@ class MyForm(settingsmixin.SMainWindow):
return self.updateStatusBar(_translate(
"MainWindow", "No addresses selected."))
- addresses_string = unicode(
- self.ui.lineEditTo.text().toUtf8(), 'utf-8')
+ addresses_string = unic(ustr(self.ui.lineEditTo.text()))
for item in selected_items:
address_string = item.accountString()
if not addresses_string:
@@ -3403,7 +3477,7 @@ class MyForm(settingsmixin.SMainWindow):
)
def on_context_menuAddressBook(self, point):
- self.popMenuAddressBook = QtGui.QMenu(self)
+ self.popMenuAddressBook = QtWidgets.QMenu(self)
self.popMenuAddressBook.addAction(self.actionAddressBookSend)
self.popMenuAddressBook.addAction(self.actionAddressBookClipboard)
self.popMenuAddressBook.addAction(self.actionAddressBookSubscribe)
@@ -3433,7 +3507,7 @@ class MyForm(settingsmixin.SMainWindow):
self.click_pushButtonAddSubscription()
def on_action_SubscriptionsDelete(self):
- if QtGui.QMessageBox.question(
+ if QtWidgets.QMessageBox.question(
self, "Delete subscription?",
_translate(
"MainWindow",
@@ -3444,12 +3518,12 @@ class MyForm(settingsmixin.SMainWindow):
" messages, but you can still view messages you"
" already received.\n\nAre you sure you want to"
" delete the subscription?"
- ), QtGui.QMessageBox.Yes | QtGui.QMessageBox.No
- ) != QtGui.QMessageBox.Yes:
+ ), QtWidgets.QMessageBox.Yes | QtWidgets.QMessageBox.No
+ ) != QtWidgets.QMessageBox.Yes:
return
address = self.getCurrentAccount()
sqlExecute('''DELETE FROM subscriptions WHERE address=?''',
- address)
+ dbstr(address))
self.rerenderTabTreeSubscriptions()
self.rerenderMessagelistFromLabels()
self.rerenderAddressBook()
@@ -3457,14 +3531,14 @@ class MyForm(settingsmixin.SMainWindow):
def on_action_SubscriptionsClipboard(self):
address = self.getCurrentAccount()
- clipboard = QtGui.QApplication.clipboard()
- clipboard.setText(str(address))
+ clipboard = QtWidgets.QApplication.clipboard()
+ clipboard.setText(ustr(address))
def on_action_SubscriptionsEnable(self):
address = self.getCurrentAccount()
sqlExecute(
'''update subscriptions set enabled=1 WHERE address=?''',
- address)
+ dbstr(address))
account = self.getCurrentItem()
account.setEnabled(True)
self.rerenderAddressBook()
@@ -3474,7 +3548,7 @@ class MyForm(settingsmixin.SMainWindow):
address = self.getCurrentAccount()
sqlExecute(
'''update subscriptions set enabled=0 WHERE address=?''',
- address)
+ dbstr(address))
account = self.getCurrentItem()
account.setEnabled(False)
self.rerenderAddressBook()
@@ -3482,7 +3556,7 @@ class MyForm(settingsmixin.SMainWindow):
def on_context_menuSubscriptions(self, point):
currentItem = self.getCurrentItem()
- self.popMenuSubscriptions = QtGui.QMenu(self)
+ self.popMenuSubscriptions = QtWidgets.QMenu(self)
if isinstance(currentItem, Ui_AddressWidget):
self.popMenuSubscriptions.addAction(self.actionsubscriptionsNew)
self.popMenuSubscriptions.addAction(self.actionsubscriptionsDelete)
@@ -3522,8 +3596,6 @@ class MyForm(settingsmixin.SMainWindow):
return self.ui.tableWidgetInboxSubscriptions
elif widget == self.ui.treeWidgetChans:
return self.ui.tableWidgetInboxChans
- else:
- return None
def getCurrentTreeWidget(self):
currentIndex = self.ui.tabWidget.currentIndex()
@@ -3576,7 +3648,7 @@ class MyForm(settingsmixin.SMainWindow):
if messagelist:
currentRow = messagelist.currentRow()
if currentRow >= 0:
- return messagelist.item(currentRow, 3).data()
+ return as_msgid(messagelist.item(currentRow, 3).data())
def getCurrentMessageTextedit(self):
currentIndex = self.ui.tabWidget.currentIndex()
@@ -3610,9 +3682,9 @@ class MyForm(settingsmixin.SMainWindow):
self.ui.inboxSearchLineEditChans,
)
if currentIndex >= 0 and currentIndex < len(messagelistList):
- return (
+ return ustr(
messagelistList[currentIndex] if retObj
- else messagelistList[currentIndex].text().toUtf8().data())
+ else ustr(messagelistList[currentIndex].text()))
def getCurrentSearchOption(self, currentIndex=None):
if currentIndex is None:
@@ -3668,7 +3740,7 @@ class MyForm(settingsmixin.SMainWindow):
if account.type == AccountMixin.NORMAL:
return # maybe in the future
elif account.type == AccountMixin.CHAN:
- if QtGui.QMessageBox.question(
+ if QtWidgets.QMessageBox.question(
self, "Delete channel?",
_translate(
"MainWindow",
@@ -3679,9 +3751,9 @@ class MyForm(settingsmixin.SMainWindow):
" messages, but you can still view messages you"
" already received.\n\nAre you sure you want to"
" delete the channel?"
- ), QtGui.QMessageBox.Yes | QtGui.QMessageBox.No
- ) == QtGui.QMessageBox.Yes:
- config.remove_section(str(account.address))
+ ), QtWidgets.QMessageBox.Yes | QtWidgets.QMessageBox.No
+ ) == QtWidgets.QMessageBox.Yes:
+ config.remove_section(ustr(account.address))
else:
return
else:
@@ -3714,15 +3786,15 @@ class MyForm(settingsmixin.SMainWindow):
account.setEnabled(False)
def disableIdentity(self, address):
- config.set(str(address), 'enabled', 'false')
+ config.set(ustr(address), 'enabled', 'false')
config.save()
shared.reloadMyAddressHashes()
self.rerenderAddressBook()
def on_action_Clipboard(self):
address = self.getCurrentAccount()
- clipboard = QtGui.QApplication.clipboard()
- clipboard.setText(str(address))
+ clipboard = QtWidgets.QApplication.clipboard()
+ clipboard.setText(ustr(address))
def on_action_ClipboardMessagelist(self):
tableWidget = self.getCurrentMessagelist()
@@ -3739,14 +3811,14 @@ class MyForm(settingsmixin.SMainWindow):
myAddress = tableWidget.item(currentRow, 0).data(QtCore.Qt.UserRole)
otherAddress = tableWidget.item(currentRow, 1).data(QtCore.Qt.UserRole)
account = accountClass(myAddress)
- if isinstance(account, GatewayAccount) and otherAddress == account.relayAddress and (
- (currentColumn in [0, 2] and self.getCurrentFolder() == "sent") or
- (currentColumn in [1, 2] and self.getCurrentFolder() != "sent")):
- text = str(tableWidget.item(currentRow, currentColumn).label)
+ if isinstance(account, GatewayAccount) \
+ and otherAddress == account.relayAddress and (
+ (currentColumn in (0, 2) and currentFolder == "sent") or
+ (currentColumn in (1, 2) and currentFolder != "sent")):
+ text = ustr(tableWidget.item(currentRow, currentColumn).label)
else:
- text = tableWidget.item(currentRow, currentColumn).data(QtCore.Qt.UserRole)
-
- clipboard = QtGui.QApplication.clipboard()
+ text = ustr(tableWidget.item(currentRow, currentColumn).data(QtCore.Qt.UserRole))
+ clipboard = QtWidgets.QApplication.clipboard()
clipboard.setText(text)
# set avatar functions
@@ -3759,8 +3831,8 @@ class MyForm(settingsmixin.SMainWindow):
def on_action_SetAvatar(self, thisTableWidget):
currentRow = thisTableWidget.currentRow()
- addressAtCurrentRow = thisTableWidget.item(
- currentRow, 1).text()
+ addressAtCurrentRow = ustr(thisTableWidget.item(
+ currentRow, 1).text())
setToIdenticon = not self.setAvatar(addressAtCurrentRow)
if setToIdenticon:
thisTableWidget.item(
@@ -3770,11 +3842,8 @@ class MyForm(settingsmixin.SMainWindow):
def setAvatar(self, addressAtCurrentRow):
if not os.path.exists(state.appdata + 'avatars/'):
os.makedirs(state.appdata + 'avatars/')
- hash = hashlib.md5(addBMIfNotPresent(addressAtCurrentRow)).hexdigest()
- extensions = [
- 'PNG', 'GIF', 'JPG', 'JPEG', 'SVG', 'BMP', 'MNG', 'PBM',
- 'PGM', 'PPM', 'TIFF', 'XBM', 'XPM', 'TGA']
-
+ hash = hashlib.md5(addBMIfNotPresent(addressAtCurrentRow).encode("utf-8", "replace")).hexdigest()
+ # http://pyqt.sourceforge.net/Docs/PyQt4/qimagereader.html#supportedImageFormats
names = {
'BMP': 'Windows Bitmap',
'GIF': 'Graphic Interchange Format',
@@ -3789,11 +3858,12 @@ class MyForm(settingsmixin.SMainWindow):
'XBM': 'X11 Bitmap',
'XPM': 'X11 Pixmap',
'SVG': 'Scalable Vector Graphics',
- 'TGA': 'Targa Image Format'}
+ 'TGA': 'Targa Image Format'
+ }
filters = []
all_images_filter = []
current_files = []
- for ext in extensions:
+ for ext in names:
filters += [names[ext] + ' (*.' + ext.lower() + ')']
all_images_filter += ['*.' + ext.lower()]
upper = state.appdata + 'avatars/' + hash + '.' + ext.upper()
@@ -3804,10 +3874,9 @@ class MyForm(settingsmixin.SMainWindow):
current_files += [upper]
filters[0:0] = ['Image files (' + ' '.join(all_images_filter) + ')']
filters[1:1] = ['All files (*.*)']
- sourcefile = QtGui.QFileDialog.getOpenFileName(
+ sourcefile = QtWidgets.QFileDialog.getOpenFileName(
self, _translate("MainWindow", "Set avatar..."),
- filter=';;'.join(filters)
- )
+ filter=';;'.join(filters))[0]
# determine the correct filename (note that avatars don't use the suffix)
destination = state.appdata + 'avatars/' + hash + '.' + sourcefile.split('.')[-1]
exists = QtCore.QFile.exists(destination)
@@ -3816,11 +3885,11 @@ class MyForm(settingsmixin.SMainWindow):
if exists | (len(current_files) > 0):
displayMsg = _translate(
"MainWindow", "Do you really want to remove this avatar?")
- overwrite = QtGui.QMessageBox.question(
+ overwrite = QtWidgets.QMessageBox.question(
self, 'Message', displayMsg,
- QtGui.QMessageBox.Yes, QtGui.QMessageBox.No)
+ QtWidgets.QMessageBox.Yes, QtWidgets.QMessageBox.No)
else:
- overwrite = QtGui.QMessageBox.No
+ overwrite = QtWidgets.QMessageBox.No
else:
# ask whether to overwrite old avatar
if exists | (len(current_files) > 0):
@@ -3828,15 +3897,15 @@ class MyForm(settingsmixin.SMainWindow):
"MainWindow",
"You have already set an avatar for this address."
" Do you really want to overwrite it?")
- overwrite = QtGui.QMessageBox.question(
+ overwrite = QtWidgets.QMessageBox.question(
self, 'Message', displayMsg,
- QtGui.QMessageBox.Yes, QtGui.QMessageBox.No)
+ QtWidgets.QMessageBox.Yes, QtWidgets.QMessageBox.No)
else:
- overwrite = QtGui.QMessageBox.No
+ overwrite = QtWidgets.QMessageBox.No
# copy the image file to the appdata folder
- if (not exists) | (overwrite == QtGui.QMessageBox.Yes):
- if overwrite == QtGui.QMessageBox.Yes:
+ if (not exists) | (overwrite == QtWidgets.QMessageBox.Yes):
+ if overwrite == QtWidgets.QMessageBox.Yes:
for file in current_files:
QtCore.QFile.remove(file)
QtCore.QFile.remove(destination)
@@ -3861,23 +3930,23 @@ class MyForm(settingsmixin.SMainWindow):
def on_action_AddressBookSetSound(self):
widget = self.ui.tableWidgetAddressBook
- self.setAddressSound(widget.item(widget.currentRow(), 0).text())
+ self.setAddressSound(ustr(widget.item(widget.currentRow(), 0).text()))
def setAddressSound(self, addr):
- filters = [unicode(_translate(
+ filters = [unic(_translate(
"MainWindow", "Sound files (%s)" %
' '.join(['*%s%s' % (os.extsep, ext) for ext in sound.extensions])
))]
- sourcefile = unicode(QtGui.QFileDialog.getOpenFileName(
+ sourcefile = unic(ustr(QtWidgets.QFileDialog.getOpenFileName(
self, _translate("MainWindow", "Set notification sound..."),
filter=';;'.join(filters)
- ))
+ )))[0]
if not sourcefile:
return
destdir = os.path.join(state.appdata, 'sounds')
- destfile = unicode(addr) + os.path.splitext(sourcefile)[-1]
+ destfile = unic(ustr(addr) + os.path.splitext(sourcefile)[-1])
destination = os.path.join(destdir, destfile)
if sourcefile == destination:
@@ -3886,15 +3955,15 @@ class MyForm(settingsmixin.SMainWindow):
pattern = destfile.lower()
for item in os.listdir(destdir):
if item.lower() == pattern:
- overwrite = QtGui.QMessageBox.question(
+ overwrite = QtWidgets.QMessageBox.question(
self, _translate("MainWindow", "Message"),
_translate(
"MainWindow",
"You have already set a notification sound"
" for this address book entry."
" Do you really want to overwrite it?"),
- QtGui.QMessageBox.Yes, QtGui.QMessageBox.No
- ) == QtGui.QMessageBox.Yes
+ QtWidgets.QMessageBox.Yes, QtWidgets.QMessageBox.No
+ ) == QtWidgets.QMessageBox.Yes
if overwrite:
QtCore.QFile.remove(os.path.join(destdir, item))
break
@@ -3905,18 +3974,23 @@ class MyForm(settingsmixin.SMainWindow):
def on_context_menuYourIdentities(self, point):
currentItem = self.getCurrentItem()
- self.popMenuYourIdentities = QtGui.QMenu(self)
+ self.popMenuYourIdentities = QtWidgets.QMenu(self)
if isinstance(currentItem, Ui_AddressWidget):
self.popMenuYourIdentities.addAction(self.actionNewYourIdentities)
self.popMenuYourIdentities.addSeparator()
- self.popMenuYourIdentities.addAction(self.actionClipboardYourIdentities)
+ self.popMenuYourIdentities.addAction(
+ self.actionClipboardYourIdentities)
self.popMenuYourIdentities.addSeparator()
if currentItem.isEnabled:
- self.popMenuYourIdentities.addAction(self.actionDisableYourIdentities)
+ self.popMenuYourIdentities.addAction(
+ self.actionDisableYourIdentities)
else:
- self.popMenuYourIdentities.addAction(self.actionEnableYourIdentities)
- self.popMenuYourIdentities.addAction(self.actionSetAvatarYourIdentities)
- self.popMenuYourIdentities.addAction(self.actionSpecialAddressBehaviorYourIdentities)
+ self.popMenuYourIdentities.addAction(
+ self.actionEnableYourIdentities)
+ self.popMenuYourIdentities.addAction(
+ self.actionSetAvatarYourIdentities)
+ self.popMenuYourIdentities.addAction(
+ self.actionSpecialAddressBehaviorYourIdentities)
self.popMenuYourIdentities.addAction(self.actionEmailGateway)
self.popMenuYourIdentities.addSeparator()
if currentItem.type != AccountMixin.ALL:
@@ -3935,7 +4009,7 @@ class MyForm(settingsmixin.SMainWindow):
# TODO make one popMenu
def on_context_menuChan(self, point):
currentItem = self.getCurrentItem()
- self.popMenu = QtGui.QMenu(self)
+ self.popMenu = QtWidgets.QMenu(self)
if isinstance(currentItem, Ui_AddressWidget):
self.popMenu.addAction(self.actionNew)
self.popMenu.addAction(self.actionDelete)
@@ -3971,7 +4045,7 @@ class MyForm(settingsmixin.SMainWindow):
self.on_context_menuSent(point)
return
- self.popMenuInbox = QtGui.QMenu(self)
+ self.popMenuInbox = QtWidgets.QMenu(self)
self.popMenuInbox.addAction(self.actionForceHtml)
self.popMenuInbox.addAction(self.actionMarkUnread)
self.popMenuInbox.addSeparator()
@@ -4006,7 +4080,7 @@ class MyForm(settingsmixin.SMainWindow):
def on_context_menuSent(self, point):
currentRow = self.ui.tableWidgetInbox.currentRow()
- self.popMenuSent = QtGui.QMenu(self)
+ self.popMenuSent = QtWidgets.QMenu(self)
self.popMenuSent.addAction(self.actionSentClipboard)
self._contact_selected = self.ui.tableWidgetInbox.item(currentRow, 0)
# preloaded gui.menu plugins with prefix 'address'
@@ -4019,10 +4093,13 @@ class MyForm(settingsmixin.SMainWindow):
# Check to see if this item is toodifficult and display an additional
# menu option (Force Send) if it is.
if currentRow >= 0:
- ackData = self.ui.tableWidgetInbox.item(currentRow, 3).data()
- queryreturn = sqlQuery('''SELECT status FROM sent where ackdata=?''', ackData)
+ ackData = as_msgid(self.ui.tableWidgetInbox.item(currentRow, 3).data())
+ queryreturn = sqlQuery('''SELECT status FROM sent where ackdata=?''', sqlite3.Binary(ackData))
+ if len(queryreturn) < 1:
+ queryreturn = sqlQuery('''SELECT status FROM sent where ackdata=CAST(? AS TEXT)''', ackData)
for row in queryreturn:
status, = row
+ status = status.decode("utf-8", "replace")
if status == 'toodifficult':
self.popMenuSent.addAction(self.actionForceSend)
@@ -4030,7 +4107,7 @@ class MyForm(settingsmixin.SMainWindow):
def inboxSearchLineEditUpdated(self, text):
# dynamic search for too short text is slow
- text = text.toUtf8()
+ text = ustr(text)
if 0 < len(text) < 3:
return
messagelist = self.getCurrentMessagelist()
@@ -4043,9 +4120,9 @@ class MyForm(settingsmixin.SMainWindow):
def inboxSearchLineEditReturnPressed(self):
logger.debug("Search return pressed")
- searchLine = self.getCurrentSearchLine()
+ searchLine = self.getCurrentSearchLine().encode('utf-8')
messagelist = self.getCurrentMessagelist()
- if messagelist and len(str(searchLine)) < 3:
+ if messagelist and len(ustr(searchLine)) < 3:
searchOption = self.getCurrentSearchOption()
account = self.getCurrentAccount()
folder = self.getCurrentFolder()
@@ -4087,7 +4164,7 @@ class MyForm(settingsmixin.SMainWindow):
if item.type == AccountMixin.ALL:
return
- newLabel = unicode(item.text(0), 'utf-8', 'ignore')
+ newLabel = unic(ustr(item.text(0)))
oldLabel = item.defaultLabel()
# unchanged, do not do anything either
@@ -4106,7 +4183,9 @@ class MyForm(settingsmixin.SMainWindow):
self.rerenderMessagelistFromLabels()
if item.type != AccountMixin.SUBSCRIPTION:
self.rerenderMessagelistToLabels()
- if item.type in (AccountMixin.NORMAL, AccountMixin.CHAN, AccountMixin.SUBSCRIPTION):
+ if item.type in (
+ AccountMixin.NORMAL, AccountMixin.CHAN, AccountMixin.SUBSCRIPTION
+ ):
self.rerenderAddressBook()
self.recurDepth -= 1
@@ -4119,17 +4198,26 @@ class MyForm(settingsmixin.SMainWindow):
folder = self.getCurrentFolder()
if msgid:
queryreturn = sqlQuery(
- '''SELECT message FROM %s WHERE %s=?''' % (
+ 'SELECT message FROM %s WHERE %s=?' % (
('sent', 'ackdata') if folder == 'sent'
else ('inbox', 'msgid')
- ), msgid
+ ), sqlite3.Binary(msgid)
)
+ if len(queryreturn) < 1:
+ queryreturn = sqlQuery(
+ 'SELECT message FROM %s WHERE %s=CAST(? AS TEXT)' % (
+ ('sent', 'ackdata') if folder == 'sent'
+ else ('inbox', 'msgid')
+ ), msgid
+ )
try:
- message = queryreturn[-1][0]
+ message = queryreturn[-1][0].decode("utf-8", "replace")
except NameError:
- message = ""
+ message = u''
except IndexError:
+ # _translate() often returns unicode, no redefinition here!
+ # pylint: disable=redefined-variable-type
message = _translate(
"MainWindow",
"Error occurred: could not load message from disk."
@@ -4141,10 +4229,16 @@ class MyForm(settingsmixin.SMainWindow):
if tableWidget.item(currentRow, 0).unread is True:
self.updateUnreadStatus(tableWidget, currentRow, msgid)
# propagate
- if folder != 'sent' and sqlExecute(
- '''UPDATE inbox SET read=1 WHERE msgid=? AND read=0''',
- msgid
- ) > 0:
+ rowcount = sqlExecute(
+ 'UPDATE inbox SET read=1 WHERE msgid=? AND read=0',
+ sqlite3.Binary(msgid)
+ )
+ if rowcount < 1:
+ rowcount = sqlExecute(
+ 'UPDATE inbox SET read=1 WHERE msgid=CAST(? AS TEXT) AND read=0',
+ msgid
+ )
+ if folder != 'sent' and rowcount > 0:
self.propagateUnreadCount()
messageTextedit.setCurrentFont(QtGui.QFont())
@@ -4158,8 +4252,9 @@ class MyForm(settingsmixin.SMainWindow):
self.rerenderMessagelistToLabels()
completerList = self.ui.lineEditTo.completer().model().stringList()
for i in range(len(completerList)):
- if unicode(completerList[i]).endswith(" <" + item.address + ">"):
- completerList[i] = item.label + " <" + item.address + ">"
+ address_block = " <" + ustr(item.address) + ">"
+ if unic(ustr(completerList[i])).endswith(unic(address_block)):
+ completerList[i] = ustr(item.label) + address_block
self.ui.lineEditTo.completer().model().setStringList(completerList)
def tabWidgetCurrentChanged(self, n):
@@ -4212,7 +4307,7 @@ class MyForm(settingsmixin.SMainWindow):
def initSettings(self):
self.loadSettings()
- for attr, obj in self.ui.__dict__.iteritems():
+ for attr, obj in six.iteritems(self.ui.__dict__):
if hasattr(obj, "__class__") and \
isinstance(obj, settingsmixin.SettingsMixin):
loadMethod = getattr(obj, "loadSettings", None)
@@ -4224,7 +4319,7 @@ app = None
myapp = None
-class BitmessageQtApplication(QtGui.QApplication):
+class BitmessageQtApplication(QtWidgets.QApplication):
"""
Listener to allow our Qt form to get focus when another instance of the
application is open.
@@ -4263,15 +4358,15 @@ class BitmessageQtApplication(QtGui.QApplication):
self.server = None
self.is_running = False
- socket = QLocalSocket()
+ socket = QtNetwork.QLocalSocket()
socket.connectToServer(id)
self.is_running = socket.waitForConnected()
# Cleanup past crashed servers
if not self.is_running:
- if socket.error() == QLocalSocket.ConnectionRefusedError:
+ if socket.error() == QtNetwork.QLocalSocket.ConnectionRefusedError:
socket.disconnectFromServer()
- QLocalServer.removeServer(id)
+ QtNetwork.QLocalServer.removeServer(id)
socket.abort()
@@ -4282,16 +4377,12 @@ class BitmessageQtApplication(QtGui.QApplication):
else:
# Nope, create a local server with this id and assign on_new_connection
# for whenever a second instance tries to run focus the application.
- self.server = QLocalServer()
+ self.server = QtNetwork.QLocalServer()
self.server.listen(id)
self.server.newConnection.connect(self.on_new_connection)
self.setStyleSheet("QStatusBar::item { border: 0px solid black }")
- def __del__(self):
- if self.server:
- self.server.close()
-
def on_new_connection(self):
if myapp:
myapp.appIndicatorShow()
diff --git a/src/bitmessageqt/account.py b/src/bitmessageqt/account.py
index 8c82c6f6..493f8a8e 100644
--- a/src/bitmessageqt/account.py
+++ b/src/bitmessageqt/account.py
@@ -1,20 +1,16 @@
-# pylint: disable=too-many-instance-attributes,attribute-defined-outside-init
"""
-account.py
-==========
-
Account related functions.
"""
-from __future__ import absolute_import
-
import inspect
import re
import sys
import time
+import sqlite3
-from PyQt4 import QtGui
+from unqstr import ustr, unic
+from dbcompat import dbstr
import queues
from addresses import decodeAddress
@@ -23,6 +19,7 @@ from helper_ackPayload import genAckPayload
from helper_sql import sqlQuery, sqlExecute
from .foldertree import AccountMixin
from .utils import str_broadcast_subscribers
+from tr import _translate
def getSortedSubscriptions(count=False):
@@ -34,22 +31,25 @@ def getSortedSubscriptions(count=False):
:retuns: dict keys are addresses, values are dicts containing settings
:rtype: dict, default {}
"""
- queryreturn = sqlQuery('SELECT label, address, enabled FROM subscriptions ORDER BY label COLLATE NOCASE ASC')
+ queryreturn = sqlQuery(
+ 'SELECT label, address, enabled FROM subscriptions'
+ ' ORDER BY label COLLATE NOCASE ASC')
ret = {}
- for row in queryreturn:
- label, address, enabled = row
- ret[address] = {}
- ret[address]["inbox"] = {}
- ret[address]["inbox"]['label'] = label
- ret[address]["inbox"]['enabled'] = enabled
- ret[address]["inbox"]['count'] = 0
+ for label, address, enabled in queryreturn:
+ label = label.decode("utf-8", "replace")
+ address = address.decode("utf-8", "replace")
+ ret[address] = {'inbox': {}}
+ ret[address]['inbox'].update(label=label, enabled=enabled, count=0)
if count:
- queryreturn = sqlQuery('''SELECT fromaddress, folder, count(msgid) as cnt
- FROM inbox, subscriptions ON subscriptions.address = inbox.fromaddress
- WHERE read = 0 AND toaddress = ?
- GROUP BY inbox.fromaddress, folder''', str_broadcast_subscribers)
- for row in queryreturn:
- address, folder, cnt = row
+ queryreturn = sqlQuery(
+ 'SELECT fromaddress, folder, count(msgid) AS cnt'
+ ' FROM inbox, subscriptions'
+ ' ON subscriptions.address = inbox.fromaddress WHERE read = 0'
+ ' AND toaddress = ? GROUP BY inbox.fromaddress, folder',
+ dbstr(str_broadcast_subscribers))
+ for address, folder, cnt in queryreturn:
+ address = address.decode("utf-8", "replace")
+ folder = folder.decode("utf-8", "replace")
if folder not in ret[address]:
ret[address][folder] = {
'label': ret[address]['inbox']['label'],
@@ -75,7 +75,8 @@ def accountClass(address):
return subscription
try:
gateway = config.get(address, "gateway")
- for _, cls in inspect.getmembers(sys.modules[__name__], inspect.isclass):
+ for _, cls in inspect.getmembers(
+ sys.modules[__name__], inspect.isclass):
if issubclass(cls, GatewayAccount) and cls.gatewayName == gateway:
return cls(address)
# general gateway
@@ -86,7 +87,7 @@ def accountClass(address):
return BMAccount(address)
-class AccountColor(AccountMixin): # pylint: disable=too-few-public-methods
+class AccountColor(AccountMixin):
"""Set the type of account"""
def __init__(self, address, address_type=None):
@@ -100,7 +101,9 @@ class AccountColor(AccountMixin): # pylint: disable=too-few-public-methods
elif config.safeGetBoolean(self.address, 'chan'):
self.type = AccountMixin.CHAN
elif sqlQuery(
- '''select label from subscriptions where address=?''', self.address):
+ 'SELECT label FROM subscriptions WHERE address=?',
+ dbstr(self.address)
+ ):
self.type = AccountMixin.SUBSCRIPTION
else:
self.type = AccountMixin.NORMAL
@@ -108,12 +111,35 @@ class AccountColor(AccountMixin): # pylint: disable=too-few-public-methods
self.type = address_type
-class BMAccount(object):
- """Encapsulate a Bitmessage account"""
-
+class NoAccount(object):
+ """Minimal account like object (All accounts)"""
+ # pylint: disable=too-many-instance-attributes
def __init__(self, address=None):
self.address = address
self.type = AccountMixin.NORMAL
+ self.toAddress = self.fromAddress = ''
+ self.subject = self.message = ''
+ self.fromLabel = self.toLabel = ''
+
+ def getLabel(self, address=None):
+ """Get a label for this bitmessage account"""
+ return address or self.address
+
+ def parseMessage(self, toAddress, fromAddress, subject, message):
+ """Set metadata and address labels on self"""
+ self.toAddress = ustr(toAddress)
+ self.fromAddress = ustr(fromAddress)
+ self.subject = ustr(subject)
+ self.message = ustr(message)
+ self.fromLabel = ustr(self.getLabel(fromAddress))
+ self.toLabel = ustr(self.getLabel(toAddress))
+
+
+class BMAccount(NoAccount):
+ """Encapsulate a Bitmessage account"""
+
+ def __init__(self, address=None):
+ super(BMAccount, self).__init__(address)
if config.has_section(address):
if config.safeGetBoolean(self.address, 'chan'):
self.type = AccountMixin.CHAN
@@ -121,55 +147,25 @@ class BMAccount(object):
self.type = AccountMixin.MAILINGLIST
elif self.address == str_broadcast_subscribers:
self.type = AccountMixin.BROADCAST
- else:
- queryreturn = sqlQuery(
- '''select label from subscriptions where address=?''', self.address)
- if queryreturn:
- self.type = AccountMixin.SUBSCRIPTION
+ elif sqlQuery(
+ 'SELECT label FROM subscriptions WHERE address=?', dbstr(self.address)
+ ):
+ self.type = AccountMixin.SUBSCRIPTION
def getLabel(self, address=None):
"""Get a label for this bitmessage account"""
- if address is None:
- address = self.address
+ address = super(BMAccount, self).getLabel(address)
label = config.safeGet(address, 'label', address)
queryreturn = sqlQuery(
- '''select label from addressbook where address=?''', address)
- if queryreturn != []:
- for row in queryreturn:
- label, = row
+ 'SELECT label FROM addressbook WHERE address=?', dbstr(address))
+ if queryreturn:
+ label = queryreturn[-1][0]
else:
queryreturn = sqlQuery(
- '''select label from subscriptions where address=?''', address)
- if queryreturn != []:
- for row in queryreturn:
- label, = row
- return label
-
- def parseMessage(self, toAddress, fromAddress, subject, message):
- """Set metadata and address labels on self"""
-
- self.toAddress = toAddress
- self.fromAddress = fromAddress
- if isinstance(subject, unicode):
- self.subject = str(subject)
- else:
- self.subject = subject
- self.message = message
- self.fromLabel = self.getLabel(fromAddress)
- self.toLabel = self.getLabel(toAddress)
-
-
-class NoAccount(BMAccount):
- """Override the __init__ method on a BMAccount"""
-
- def __init__(self, address=None): # pylint: disable=super-init-not-called
- self.address = address
- self.type = AccountMixin.NORMAL
-
- def getLabel(self, address=None):
- if address is None:
- address = self.address
- return address
+ 'SELECT label FROM subscriptions WHERE address=?', dbstr(address))
+ if queryreturn:
+ label = queryreturn[-1][0]
+ return unic(ustr(label))
class SubscriptionAccount(BMAccount):
@@ -189,31 +185,27 @@ class GatewayAccount(BMAccount):
ALL_OK = 0
REGISTRATION_DENIED = 1
- def __init__(self, address):
- super(GatewayAccount, self).__init__(address)
-
def send(self):
- """Override the send method for gateway accounts"""
-
- # pylint: disable=unused-variable
- status, addressVersionNumber, streamNumber, ripe = decodeAddress(self.toAddress)
- stealthLevel = config.safeGetInt('bitmessagesettings', 'ackstealthlevel')
+ """The send method for gateway accounts"""
+ streamNumber, ripe = decodeAddress(self.toAddress)[2:]
+ stealthLevel = config.safeGetInt(
+ 'bitmessagesettings', 'ackstealthlevel')
ackdata = genAckPayload(streamNumber, stealthLevel)
sqlExecute(
'''INSERT INTO sent VALUES (?,?,?,?,?,?,?,?,?,?,?,?,?,?,?)''',
- '',
- self.toAddress,
- ripe,
- self.fromAddress,
- self.subject,
- self.message,
- ackdata,
+ sqlite3.Binary(b''),
+ dbstr(self.toAddress),
+ sqlite3.Binary(ripe),
+ dbstr(self.fromAddress),
+ dbstr(self.subject),
+ dbstr(self.message),
+ sqlite3.Binary(ackdata),
int(time.time()), # sentTime (this will never change)
int(time.time()), # lastActionTime
0, # sleepTill time. This will get set when the POW gets done.
- 'msgqueued',
+ dbstr('msgqueued'),
0, # retryNumber
- 'sent', # folder
+ dbstr('sent'), # folder
2, # encodingtype
# not necessary to have a TTL higher than 2 days
min(config.getint('bitmessagesettings', 'ttl'), 86400 * 2)
@@ -270,10 +262,9 @@ class MailchuckAccount(GatewayAccount):
def settings(self):
"""settings specific to a MailchuckAccount"""
-
self.toAddress = self.registrationAddress
self.subject = "config"
- self.message = QtGui.QApplication.translate(
+ self.message = _translate(
"Mailchuck",
"""# You can use this to configure your email gateway account
# Uncomment the setting you want to use
@@ -319,8 +310,9 @@ class MailchuckAccount(GatewayAccount):
def parseMessage(self, toAddress, fromAddress, subject, message):
"""parseMessage specific to a MailchuckAccount"""
-
- super(MailchuckAccount, self).parseMessage(toAddress, fromAddress, subject, message)
+ super(MailchuckAccount, self).parseMessage(
+ toAddress, fromAddress, subject, message
+ )
if fromAddress == self.relayAddress:
matches = self.regExpIncoming.search(subject)
if matches is not None:
@@ -341,6 +333,7 @@ class MailchuckAccount(GatewayAccount):
self.toLabel = matches.group(1)
self.toAddress = matches.group(1)
self.feedback = self.ALL_OK
- if fromAddress == self.registrationAddress and self.subject == "Registration Request Denied":
+ if fromAddress == self.registrationAddress \
+ and self.subject == "Registration Request Denied":
self.feedback = self.REGISTRATION_DENIED
return self.feedback
diff --git a/src/bitmessageqt/address_dialogs.py b/src/bitmessageqt/address_dialogs.py
index bf571041..5986379a 100644
--- a/src/bitmessageqt/address_dialogs.py
+++ b/src/bitmessageqt/address_dialogs.py
@@ -1,40 +1,40 @@
"""
Dialogs that work with BM address.
"""
-# pylint: disable=attribute-defined-outside-init,too-few-public-methods,relative-import
+# pylint: disable=too-few-public-methods
+# https://github.com/PyCQA/pylint/issues/471
import hashlib
-from PyQt4 import QtCore, QtGui
+from unqstr import ustr, unic
+from qtpy import QtGui, QtWidgets
import queues
-import widgets
+from bitmessageqt import widgets
import state
-from account import AccountMixin, GatewayAccount, MailchuckAccount, accountClass
+from .account import (
+ GatewayAccount, MailchuckAccount, AccountMixin, accountClass
+)
from addresses import addBMIfNotPresent, decodeAddress, encodeVarint
from bmconfigparser import config as global_config
from tr import _translate
class AddressCheckMixin(object):
- """Base address validation class for QT UI"""
+ """Base address validation class for Qt UI"""
- def __init__(self):
+ def _setup(self):
self.valid = False
- QtCore.QObject.connect( # pylint: disable=no-member
- self.lineEditAddress,
- QtCore.SIGNAL("textChanged(QString)"),
- self.addressChanged)
+ self.lineEditAddress.textChanged.connect(self.addressChanged)
def _onSuccess(self, addressVersion, streamNumber, ripe):
pass
- def addressChanged(self, QString):
+ def addressChanged(self, address):
"""
Address validation callback, performs validation and gives feedback
"""
- status, addressVersion, streamNumber, ripe = decodeAddress(
- str(QString))
+ status, addressVersion, streamNumber, ripe = decodeAddress(ustr(address))
self.valid = status == 'success'
if self.valid:
self.labelAddressCheck.setText(
@@ -79,19 +79,27 @@ class AddressCheckMixin(object):
))
-class AddressDataDialog(QtGui.QDialog, AddressCheckMixin):
- """QDialog with Bitmessage address validation"""
+class AddressDataDialog(QtWidgets.QDialog, AddressCheckMixin):
+ """
+ Base class for a dialog getting BM-address data.
+ Corresponding ui-file should define two fields:
+ lineEditAddress - for the address
+ lineEditLabel - for it's label
+ After address validation the values of that fields are put into
+ the data field of the dialog.
+ """
def __init__(self, parent):
super(AddressDataDialog, self).__init__(parent)
self.parent = parent
+ self.data = None
def accept(self):
- """Callback for QDIalog accepting value"""
+ """Callback for QDialog accepting value"""
if self.valid:
self.data = (
- addBMIfNotPresent(str(self.lineEditAddress.text())),
- str(self.lineEditLabel.text().toUtf8())
+ addBMIfNotPresent(ustr(self.lineEditAddress.text())),
+ ustr(self.lineEditLabel.text())
)
else:
queues.UISignalQueue.put(('updateStatusBar', _translate(
@@ -107,12 +115,12 @@ class AddAddressDialog(AddressDataDialog):
def __init__(self, parent=None, address=None):
super(AddAddressDialog, self).__init__(parent)
widgets.load('addaddressdialog.ui', self)
- AddressCheckMixin.__init__(self)
+ self._setup()
if address:
self.lineEditAddress.setText(address)
-class NewAddressDialog(QtGui.QDialog):
+class NewAddressDialog(QtWidgets.QDialog):
"""QDialog for generating a new address"""
def __init__(self, parent=None):
@@ -125,7 +133,7 @@ class NewAddressDialog(QtGui.QDialog):
self.radioButtonExisting.click()
self.comboBoxExisting.addItem(address)
self.groupBoxDeterministic.setHidden(True)
- QtGui.QWidget.resize(self, QtGui.QWidget.sizeHint(self))
+ QtWidgets.QWidget.resize(self, QtWidgets.QWidget.sizeHint(self))
self.show()
def accept(self):
@@ -142,13 +150,13 @@ class NewAddressDialog(QtGui.QDialog):
self.comboBoxExisting.currentText())[2]
queues.addressGeneratorQueue.put((
'createRandomAddress', 4, streamNumberForAddress,
- str(self.newaddresslabel.text().toUtf8()), 1, "",
+ ustr(self.newaddresslabel.text()), 1, "",
self.checkBoxEighteenByteRipe.isChecked()
))
else:
- if self.lineEditPassphrase.text() != \
- self.lineEditPassphraseAgain.text():
- QtGui.QMessageBox.about(
+ if ustr(self.lineEditPassphrase.text()) != \
+ ustr(self.lineEditPassphraseAgain.text()):
+ QtWidgets.QMessageBox.about(
self, _translate("MainWindow", "Passphrase mismatch"),
_translate(
"MainWindow",
@@ -156,7 +164,7 @@ class NewAddressDialog(QtGui.QDialog):
" match. Try again.")
)
elif self.lineEditPassphrase.text() == "":
- QtGui.QMessageBox.about(
+ QtWidgets.QMessageBox.about(
self, _translate("MainWindow", "Choose a passphrase"),
_translate(
"MainWindow", "You really do need a passphrase.")
@@ -169,7 +177,7 @@ class NewAddressDialog(QtGui.QDialog):
'createDeterministicAddresses', 4, streamNumberForAddress,
"unused deterministic address",
self.spinBoxNumberOfAddressesToMake.value(),
- self.lineEditPassphrase.text().toUtf8(),
+ ustr(self.lineEditPassphrase.text()),
self.checkBoxEighteenByteRipe.isChecked()
))
@@ -180,7 +188,8 @@ class NewSubscriptionDialog(AddressDataDialog):
def __init__(self, parent=None):
super(NewSubscriptionDialog, self).__init__(parent)
widgets.load('newsubscriptiondialog.ui', self)
- AddressCheckMixin.__init__(self)
+ self.recent = []
+ self._setup()
def _onSuccess(self, addressVersion, streamNumber, ripe):
if addressVersion <= 3:
@@ -211,22 +220,21 @@ class NewSubscriptionDialog(AddressDataDialog):
_translate(
"MainWindow",
"Display the %n recent broadcast(s) from this address.",
- None,
- QtCore.QCoreApplication.CodecForTr,
- count
+ None, count
))
-class RegenerateAddressesDialog(QtGui.QDialog):
+class RegenerateAddressesDialog(QtWidgets.QDialog):
"""QDialog for regenerating deterministic addresses"""
+
def __init__(self, parent=None):
super(RegenerateAddressesDialog, self).__init__(parent)
widgets.load('regenerateaddresses.ui', self)
self.groupBox.setTitle('')
- QtGui.QWidget.resize(self, QtGui.QWidget.sizeHint(self))
+ QtWidgets.QWidget.resize(self, QtWidgets.QWidget.sizeHint(self))
-class SpecialAddressBehaviorDialog(QtGui.QDialog):
+class SpecialAddressBehaviorDialog(QtWidgets.QDialog):
"""
QDialog for special address behaviour (e.g. mailing list functionality)
"""
@@ -234,7 +242,7 @@ class SpecialAddressBehaviorDialog(QtGui.QDialog):
def __init__(self, parent=None, config=global_config):
super(SpecialAddressBehaviorDialog, self).__init__(parent)
widgets.load('specialaddressbehavior.ui', self)
- self.address = parent.getCurrentAccount()
+ self.address = ustr(parent.getCurrentAccount())
self.parent = parent
self.config = config
@@ -257,12 +265,12 @@ class SpecialAddressBehaviorDialog(QtGui.QDialog):
self.radioButtonBehaviorMailingList.click()
else:
self.radioButtonBehaveNormalAddress.click()
- mailingListName = config.safeGet(self.address, 'mailinglistname', '')
+ mailingListName = config.safeGet(
+ self.address, 'mailinglistname', '')
self.lineEditMailingListName.setText(
- unicode(mailingListName, 'utf-8')
- )
+ unic(ustr(mailingListName)))
- QtGui.QWidget.resize(self, QtGui.QWidget.sizeHint(self))
+ QtWidgets.QWidget.resize(self, QtWidgets.QWidget.sizeHint(self))
self.show()
def accept(self):
@@ -271,18 +279,18 @@ class SpecialAddressBehaviorDialog(QtGui.QDialog):
if self.address_is_chan:
return
if self.radioButtonBehaveNormalAddress.isChecked():
- self.config.set(str(self.address), 'mailinglist', 'false')
+ self.config.set(self.address, 'mailinglist', 'false')
# Set the color to either black or grey
if self.config.getboolean(self.address, 'enabled'):
self.parent.setCurrentItemColor(
- QtGui.QApplication.palette().text().color()
+ QtWidgets.QApplication.palette().text().color()
)
else:
self.parent.setCurrentItemColor(QtGui.QColor(128, 128, 128))
else:
- self.config.set(str(self.address), 'mailinglist', 'true')
- self.config.set(str(self.address), 'mailinglistname', str(
- self.lineEditMailingListName.text().toUtf8()))
+ self.config.set(self.address, 'mailinglist', 'true')
+ self.config.set(self.address, 'mailinglistname', ustr(
+ self.lineEditMailingListName.text()))
self.parent.setCurrentItemColor(
QtGui.QColor(137, 4, 177)) # magenta
self.parent.rerenderComboBoxSendFrom()
@@ -291,13 +299,15 @@ class SpecialAddressBehaviorDialog(QtGui.QDialog):
self.parent.rerenderMessagelistToLabels()
-class EmailGatewayDialog(QtGui.QDialog):
+class EmailGatewayDialog(QtWidgets.QDialog):
"""QDialog for email gateway control"""
+
def __init__(self, parent, config=global_config, account=None):
super(EmailGatewayDialog, self).__init__(parent)
widgets.load('emailgateway.ui', self)
self.parent = parent
self.config = config
+ self.data = None
if account:
self.acct = account
self.setWindowTitle(_translate(
@@ -330,7 +340,7 @@ class EmailGatewayDialog(QtGui.QDialog):
else:
self.acct = MailchuckAccount(address)
self.lineEditEmail.setFocus()
- QtGui.QWidget.resize(self, QtGui.QWidget.sizeHint(self))
+ QtWidgets.QWidget.resize(self, QtWidgets.QWidget.sizeHint(self))
def accept(self):
"""Accept callback"""
@@ -344,7 +354,7 @@ class EmailGatewayDialog(QtGui.QDialog):
if self.radioButtonRegister.isChecked() \
or self.radioButtonRegister.isHidden():
- email = str(self.lineEditEmail.text().toUtf8())
+ email = ustr(self.lineEditEmail.text())
self.acct.register(email)
self.config.set(self.acct.fromAddress, 'label', email)
self.config.set(self.acct.fromAddress, 'gateway', 'mailchuck')
diff --git a/src/bitmessageqt/addressvalidator.py b/src/bitmessageqt/addressvalidator.py
index dc61b41c..d6cbf7c1 100644
--- a/src/bitmessageqt/addressvalidator.py
+++ b/src/bitmessageqt/addressvalidator.py
@@ -1,38 +1,36 @@
"""
-Address validator module.
+The validator for address and passphrase QLineEdits
+used in `.dialogs.NewChanDialog`.
"""
-# pylint: disable=too-many-branches,too-many-arguments
+# pylint: disable=too-many-arguments
-from Queue import Empty
+from six.moves.queue import Empty
-from PyQt4 import QtGui
+from unqstr import ustr
+from qtpy import QtGui
from addresses import decodeAddress, addBMIfNotPresent
from bmconfigparser import config
from queues import apiAddressGeneratorReturnQueue, addressGeneratorQueue
from tr import _translate
-from utils import str_chan
+from .utils import str_chan
class AddressPassPhraseValidatorMixin(object):
"""Bitmessage address or passphrase validator class for Qt UI"""
def setParams(
- self,
- passPhraseObject=None,
- addressObject=None,
- feedBackObject=None,
- buttonBox=None,
- addressMandatory=True,
+ self, passPhraseObject=None, addressObject=None,
+ feedBackObject=None, button=None, addressMandatory=True
):
- """Initialisation"""
+ """Initialization"""
self.addressObject = addressObject
self.passPhraseObject = passPhraseObject
self.feedBackObject = feedBackObject
- self.buttonBox = buttonBox
self.addressMandatory = addressMandatory
self.isValid = False
# save default text
- self.okButtonLabel = self.buttonBox.button(QtGui.QDialogButtonBox.Ok).text()
+ self.okButton = button
+ self.okButtonLabel = button.text()
def setError(self, string):
"""Indicate that the validation is pending or failed"""
@@ -43,13 +41,13 @@ class AddressPassPhraseValidatorMixin(object):
self.feedBackObject.setStyleSheet("QLabel { color : red; }")
self.feedBackObject.setText(string)
self.isValid = False
- if self.buttonBox:
- self.buttonBox.button(QtGui.QDialogButtonBox.Ok).setEnabled(False)
+ if self.okButton:
+ self.okButton.setEnabled(False)
if string is not None and self.feedBackObject is not None:
- self.buttonBox.button(QtGui.QDialogButtonBox.Ok).setText(
+ self.okButton.setText(
_translate("AddressValidator", "Invalid"))
else:
- self.buttonBox.button(QtGui.QDialogButtonBox.Ok).setText(
+ self.okButton.setText(
_translate("AddressValidator", "Validating..."))
def setOK(self, string):
@@ -61,9 +59,9 @@ class AddressPassPhraseValidatorMixin(object):
self.feedBackObject.setStyleSheet("QLabel { }")
self.feedBackObject.setText(string)
self.isValid = True
- if self.buttonBox:
- self.buttonBox.button(QtGui.QDialogButtonBox.Ok).setEnabled(True)
- self.buttonBox.button(QtGui.QDialogButtonBox.Ok).setText(self.okButtonLabel)
+ if self.okButton:
+ self.okButton.setEnabled(True)
+ self.okButton.setText(self.okButtonLabel)
def checkQueue(self):
"""Validator queue loop"""
@@ -76,7 +74,8 @@ class AddressPassPhraseValidatorMixin(object):
while True:
try:
- addressGeneratorReturnValue = apiAddressGeneratorReturnQueue.get(False)
+ addressGeneratorReturnValue = \
+ apiAddressGeneratorReturnQueue.get(False)
except Empty:
if gotOne:
break
@@ -86,96 +85,120 @@ class AddressPassPhraseValidatorMixin(object):
gotOne = True
if not addressGeneratorReturnValue:
- self.setError(_translate("AddressValidator", "Address already present as one of your identities."))
- return (QtGui.QValidator.Intermediate, 0)
- if addressGeneratorReturnValue[0] == 'chan name does not match address':
- self.setError(
- _translate(
- "AddressValidator",
- "Although the Bitmessage address you "
- "entered was valid, it doesn't match the chan name."))
- return (QtGui.QValidator.Intermediate, 0)
- self.setOK(_translate("MainWindow", "Passphrase and address appear to be valid."))
+ self.setError(_translate(
+ "AddressValidator",
+ "Address already present as one of your identities."
+ ))
+ return
+ if addressGeneratorReturnValue[0] == \
+ 'chan name does not match address':
+ self.setError(_translate(
+ "AddressValidator",
+ "Although the Bitmessage address you entered was valid,"
+ " it doesn\'t match the chan name."
+ ))
+ return
+ self.setOK(_translate(
+ "MainWindow", "Passphrase and address appear to be valid."))
def returnValid(self):
"""Return the value of whether the validation was successful"""
- if self.isValid:
- return QtGui.QValidator.Acceptable
- return QtGui.QValidator.Intermediate
+ return QtGui.QValidator.Acceptable if self.isValid \
+ else QtGui.QValidator.Intermediate
def validate(self, s, pos):
"""Top level validator method"""
- if self.addressObject is None:
+ try:
+ address = ustr(self.addressObject.text())
+ except AttributeError:
address = None
- else:
- address = str(self.addressObject.text().toUtf8())
- if address == "":
- address = None
- if self.passPhraseObject is None:
+ try:
+ passPhrase = ustr(self.passPhraseObject.text())
+ except AttributeError:
passPhrase = ""
- else:
- passPhrase = str(self.passPhraseObject.text().toUtf8())
- if passPhrase == "":
- passPhrase = None
# no chan name
- if passPhrase is None:
- self.setError(_translate("AddressValidator", "Chan name/passphrase needed. You didn't enter a chan name."))
- return (QtGui.QValidator.Intermediate, pos)
+ if not passPhrase:
+ self.setError(_translate(
+ "AddressValidator",
+ "Chan name/passphrase needed. You didn't enter a chan name."
+ ))
+ return (QtGui.QValidator.Intermediate, s, pos)
- if self.addressMandatory or address is not None:
+ if self.addressMandatory or address:
# check if address already exists:
- if address in config.addresses():
- self.setError(_translate("AddressValidator", "Address already present as one of your identities."))
- return (QtGui.QValidator.Intermediate, pos)
+ if address in config.addresses(True):
+ self.setError(_translate(
+ "AddressValidator",
+ "Address already present as one of your identities."
+ ))
+ return (QtGui.QValidator.Intermediate, s, pos)
+ status = decodeAddress(address)[0]
# version too high
- if decodeAddress(address)[0] == 'versiontoohigh':
- self.setError(
- _translate(
- "AddressValidator",
- "Address too new. Although that Bitmessage"
- " address might be valid, its version number"
- " is too new for us to handle. Perhaps you need"
- " to upgrade Bitmessage."))
- return (QtGui.QValidator.Intermediate, pos)
-
+ if status == 'versiontoohigh':
+ self.setError(_translate(
+ "AddressValidator",
+ "Address too new. Although that Bitmessage address"
+ " might be valid, its version number is too new"
+ " for us to handle. Perhaps you need to upgrade"
+ " Bitmessage."
+ ))
+ return (QtGui.QValidator.Intermediate, s, pos)
# invalid
- if decodeAddress(address)[0] != 'success':
- self.setError(_translate("AddressValidator", "The Bitmessage address is not valid."))
- return (QtGui.QValidator.Intermediate, pos)
+ if status != 'success':
+ self.setError(_translate(
+ "AddressValidator",
+ "The Bitmessage address is not valid."
+ ))
+ return (QtGui.QValidator.Intermediate, s, pos)
# this just disables the OK button without changing the feedback text
# but only if triggered by textEdited, not by clicking the Ok button
- if not self.buttonBox.button(QtGui.QDialogButtonBox.Ok).hasFocus():
+ if not self.okButton.hasFocus():
self.setError(None)
# check through generator
- if address is None:
- addressGeneratorQueue.put(('createChan', 4, 1, str_chan + ' ' + str(passPhrase), passPhrase, False))
+ if not address:
+ addressGeneratorQueue.put((
+ 'createChan', 4, 1,
+ str_chan + ' ' + passPhrase, passPhrase.encode("utf-8", "replace"), False
+ ))
else:
- addressGeneratorQueue.put(
- ('joinChan', addBMIfNotPresent(address),
- "{} {}".format(str_chan, passPhrase), passPhrase, False))
+ addressGeneratorQueue.put((
+ 'joinChan', addBMIfNotPresent(address),
+ "{} {}".format(str_chan, passPhrase), passPhrase.encode("utf-8", "replace"), False
+ ))
- if self.buttonBox.button(QtGui.QDialogButtonBox.Ok).hasFocus():
- return (self.returnValid(), pos)
- return (QtGui.QValidator.Intermediate, pos)
+ if self.okButton.hasFocus():
+ return (self.returnValid(), s, pos)
+ else:
+ return (QtGui.QValidator.Intermediate, s, pos)
def checkData(self):
"""Validator Qt signal interface"""
- return self.validate("", 0)
+ return self.validate(u"", 0)
class AddressValidator(QtGui.QValidator, AddressPassPhraseValidatorMixin):
"""AddressValidator class for Qt UI"""
- def __init__(self, parent=None, passPhraseObject=None, feedBackObject=None, buttonBox=None, addressMandatory=True):
+ def __init__(
+ self, parent=None, passPhraseObject=None, feedBackObject=None,
+ button=None, addressMandatory=True
+ ):
super(AddressValidator, self).__init__(parent)
- self.setParams(passPhraseObject, parent, feedBackObject, buttonBox, addressMandatory)
+ self.setParams(
+ passPhraseObject, parent, feedBackObject, button,
+ addressMandatory)
class PassPhraseValidator(QtGui.QValidator, AddressPassPhraseValidatorMixin):
"""PassPhraseValidator class for Qt UI"""
- def __init__(self, parent=None, addressObject=None, feedBackObject=None, buttonBox=None, addressMandatory=False):
+ def __init__(
+ self, parent=None, addressObject=None, feedBackObject=None,
+ button=None, addressMandatory=False
+ ):
super(PassPhraseValidator, self).__init__(parent)
- self.setParams(parent, addressObject, feedBackObject, buttonBox, addressMandatory)
+ self.setParams(
+ parent, addressObject, feedBackObject, button,
+ addressMandatory)
diff --git a/src/bitmessageqt/bitmessage_icons_rc.py b/src/bitmessageqt/bitmessage_icons_rc.py
index bb0a02c0..6f9b20a0 100644
--- a/src/bitmessageqt/bitmessage_icons_rc.py
+++ b/src/bitmessageqt/bitmessage_icons_rc.py
@@ -7,9 +7,9 @@
#
# WARNING! All changes made in this file will be lost!
-from PyQt4 import QtCore
+from qtpy import QtCore
-qt_resource_data = "\
+qt_resource_data = b"\
\x00\x00\x03\x66\
\x89\
\x50\x4e\x47\x0d\x0a\x1a\x0a\x00\x00\x00\x0d\x49\x48\x44\x52\x00\
@@ -1534,7 +1534,7 @@ qt_resource_data = "\
\x82\
"
-qt_resource_name = "\
+qt_resource_name = b"\
\x00\x09\
\x0c\x78\x54\x88\
\x00\x6e\
@@ -1639,7 +1639,7 @@ qt_resource_name = "\
\x00\x70\x00\x6e\x00\x67\
"
-qt_resource_struct = "\
+qt_resource_struct = b"\
\x00\x00\x00\x00\x00\x02\x00\x00\x00\x01\x00\x00\x00\x01\
\x00\x00\x00\x00\x00\x02\x00\x00\x00\x01\x00\x00\x00\x02\
\x00\x00\x00\x18\x00\x02\x00\x00\x00\x15\x00\x00\x00\x03\
@@ -1666,10 +1666,15 @@ qt_resource_struct = "\
\x00\x00\x01\xe6\x00\x00\x00\x00\x00\x01\x00\x00\x34\xdf\
"
+
def qInitResources():
- QtCore.qRegisterResourceData(0x01, qt_resource_struct, qt_resource_name, qt_resource_data)
+ QtCore.qRegisterResourceData(
+ 0x01, qt_resource_struct, qt_resource_name, qt_resource_data)
+
def qCleanupResources():
- QtCore.qUnregisterResourceData(0x01, qt_resource_struct, qt_resource_name, qt_resource_data)
+ QtCore.qUnregisterResourceData(
+ 0x01, qt_resource_struct, qt_resource_name, qt_resource_data)
+
qInitResources()
diff --git a/src/bitmessageqt/bitmessageui.py b/src/bitmessageqt/bitmessageui.py
index bee8fd57..1a98d4d4 100644
--- a/src/bitmessageqt/bitmessageui.py
+++ b/src/bitmessageqt/bitmessageui.py
@@ -1,59 +1,32 @@
-# -*- coding: utf-8 -*-
+# pylint: skip-file
+# flake8: noqa
-# Form implementation generated from reading ui file 'bitmessageui.ui'
-#
-# Created: Mon Mar 23 22:18:07 2015
-# by: PyQt4 UI code generator 4.10.4
-#
-# WARNING! All changes made in this file will be lost!
-
-from PyQt4 import QtCore, QtGui
+from qtpy import QtCore, QtGui, QtWidgets
+from tr import _translate
from bmconfigparser import config
-from foldertree import AddressBookCompleter
-from messageview import MessageView
-from messagecompose import MessageCompose
-import settingsmixin
-from networkstatus import NetworkStatus
-from blacklist import Blacklist
-
-try:
- _fromUtf8 = QtCore.QString.fromUtf8
-except AttributeError:
- def _fromUtf8(s):
- return s
-
-try:
- _encoding = QtGui.QApplication.UnicodeUTF8
-
- def _translate(context, text, disambig, encoding=QtCore.QCoreApplication.CodecForTr, n=None):
- if n is None:
- return QtGui.QApplication.translate(context, text, disambig, _encoding)
- else:
- return QtGui.QApplication.translate(context, text, disambig, _encoding, n)
-except AttributeError:
- def _translate(context, text, disambig, encoding=QtCore.QCoreApplication.CodecForTr, n=None):
- if n is None:
- return QtGui.QApplication.translate(context, text, disambig)
- else:
- return QtGui.QApplication.translate(context, text, disambig, QtCore.QCoreApplication.CodecForTr, n)
+from .foldertree import AddressBookCompleter
+from .messageview import MessageView
+from .messagecompose import MessageCompose
+from bitmessageqt import settingsmixin
+from .networkstatus import NetworkStatus
+from .blacklist import Blacklist
+from bitmessageqt import bitmessage_icons_rc
class Ui_MainWindow(object):
def setupUi(self, MainWindow):
- MainWindow.setObjectName(_fromUtf8("MainWindow"))
+ MainWindow.setObjectName("MainWindow")
MainWindow.resize(885, 580)
icon = QtGui.QIcon()
- icon.addPixmap(
- QtGui.QPixmap(_fromUtf8(":/newPrefix/images/can-icon-24px.png")), QtGui.QIcon.Normal, QtGui.QIcon.Off
- )
+ icon.addPixmap(QtGui.QPixmap(":/newPrefix/images/can-icon-24px.png"), QtGui.QIcon.Normal, QtGui.QIcon.Off)
MainWindow.setWindowIcon(icon)
- MainWindow.setTabShape(QtGui.QTabWidget.Rounded)
- self.centralwidget = QtGui.QWidget(MainWindow)
- self.centralwidget.setObjectName(_fromUtf8("centralwidget"))
- self.gridLayout_10 = QtGui.QGridLayout(self.centralwidget)
- self.gridLayout_10.setObjectName(_fromUtf8("gridLayout_10"))
- self.tabWidget = QtGui.QTabWidget(self.centralwidget)
- sizePolicy = QtGui.QSizePolicy(QtGui.QSizePolicy.Expanding, QtGui.QSizePolicy.Expanding)
+ MainWindow.setTabShape(QtWidgets.QTabWidget.Rounded)
+ self.centralwidget = QtWidgets.QWidget(MainWindow)
+ self.centralwidget.setObjectName("centralwidget")
+ self.gridLayout_10 = QtWidgets.QGridLayout(self.centralwidget)
+ self.gridLayout_10.setObjectName("gridLayout_10")
+ self.tabWidget = QtWidgets.QTabWidget(self.centralwidget)
+ sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Expanding, QtWidgets.QSizePolicy.Expanding)
sizePolicy.setHorizontalStretch(0)
sizePolicy.setVerticalStretch(0)
sizePolicy.setHeightForWidth(self.tabWidget.sizePolicy().hasHeightForWidth())
@@ -61,32 +34,30 @@ class Ui_MainWindow(object):
self.tabWidget.setMinimumSize(QtCore.QSize(0, 0))
self.tabWidget.setBaseSize(QtCore.QSize(0, 0))
font = QtGui.QFont()
- base_size = QtGui.QApplication.instance().font().pointSize()
+ base_size = QtWidgets.QApplication.instance().font().pointSize()
font.setPointSize(int(base_size * 0.75))
self.tabWidget.setFont(font)
- self.tabWidget.setTabPosition(QtGui.QTabWidget.North)
- self.tabWidget.setTabShape(QtGui.QTabWidget.Rounded)
- self.tabWidget.setObjectName(_fromUtf8("tabWidget"))
- self.inbox = QtGui.QWidget()
- self.inbox.setObjectName(_fromUtf8("inbox"))
- self.gridLayout = QtGui.QGridLayout(self.inbox)
- self.gridLayout.setObjectName(_fromUtf8("gridLayout"))
+ self.tabWidget.setTabPosition(QtWidgets.QTabWidget.North)
+ self.tabWidget.setTabShape(QtWidgets.QTabWidget.Rounded)
+ self.tabWidget.setObjectName("tabWidget")
+ self.inbox = QtWidgets.QWidget()
+ self.inbox.setObjectName("inbox")
+ self.gridLayout = QtWidgets.QGridLayout(self.inbox)
+ self.gridLayout.setObjectName("gridLayout")
self.horizontalSplitter_3 = settingsmixin.SSplitter()
- self.horizontalSplitter_3.setObjectName(_fromUtf8("horizontalSplitter_3"))
+ self.horizontalSplitter_3.setObjectName("horizontalSplitter_3")
self.verticalSplitter_12 = settingsmixin.SSplitter()
- self.verticalSplitter_12.setObjectName(_fromUtf8("verticalSplitter_12"))
+ self.verticalSplitter_12.setObjectName("verticalSplitter_12")
self.verticalSplitter_12.setOrientation(QtCore.Qt.Vertical)
self.treeWidgetYourIdentities = settingsmixin.STreeWidget(self.inbox)
- self.treeWidgetYourIdentities.setObjectName(_fromUtf8("treeWidgetYourIdentities"))
+ self.treeWidgetYourIdentities.setObjectName("treeWidgetYourIdentities")
self.treeWidgetYourIdentities.resize(200, self.treeWidgetYourIdentities.height())
icon1 = QtGui.QIcon()
- icon1.addPixmap(
- QtGui.QPixmap(_fromUtf8(":/newPrefix/images/identities.png")), QtGui.QIcon.Selected, QtGui.QIcon.Off
- )
+ icon1.addPixmap(QtGui.QPixmap(":/newPrefix/images/identities.png"), QtGui.QIcon.Selected, QtGui.QIcon.Off)
self.treeWidgetYourIdentities.headerItem().setIcon(0, icon1)
self.verticalSplitter_12.addWidget(self.treeWidgetYourIdentities)
- self.pushButtonNewAddress = QtGui.QPushButton(self.inbox)
- self.pushButtonNewAddress.setObjectName(_fromUtf8("pushButtonNewAddress"))
+ self.pushButtonNewAddress = QtWidgets.QPushButton(self.inbox)
+ self.pushButtonNewAddress.setObjectName("pushButtonNewAddress")
self.pushButtonNewAddress.resize(200, self.pushButtonNewAddress.height())
self.verticalSplitter_12.addWidget(self.pushButtonNewAddress)
self.verticalSplitter_12.setStretchFactor(0, 1)
@@ -96,21 +67,21 @@ class Ui_MainWindow(object):
self.verticalSplitter_12.handle(1).setEnabled(False)
self.horizontalSplitter_3.addWidget(self.verticalSplitter_12)
self.verticalSplitter_7 = settingsmixin.SSplitter()
- self.verticalSplitter_7.setObjectName(_fromUtf8("verticalSplitter_7"))
+ self.verticalSplitter_7.setObjectName("verticalSplitter_7")
self.verticalSplitter_7.setOrientation(QtCore.Qt.Vertical)
- self.horizontalSplitterSearch = QtGui.QSplitter()
- self.horizontalSplitterSearch.setObjectName(_fromUtf8("horizontalSplitterSearch"))
- self.inboxSearchLineEdit = QtGui.QLineEdit(self.inbox)
- self.inboxSearchLineEdit.setObjectName(_fromUtf8("inboxSearchLineEdit"))
+ self.horizontalSplitterSearch = QtWidgets.QSplitter()
+ self.horizontalSplitterSearch.setObjectName("horizontalSplitterSearch")
+ self.inboxSearchLineEdit = QtWidgets.QLineEdit(self.inbox)
+ self.inboxSearchLineEdit.setObjectName("inboxSearchLineEdit")
self.horizontalSplitterSearch.addWidget(self.inboxSearchLineEdit)
- self.inboxSearchOption = QtGui.QComboBox(self.inbox)
- self.inboxSearchOption.setObjectName(_fromUtf8("inboxSearchOption"))
- self.inboxSearchOption.addItem(_fromUtf8(""))
- self.inboxSearchOption.addItem(_fromUtf8(""))
- self.inboxSearchOption.addItem(_fromUtf8(""))
- self.inboxSearchOption.addItem(_fromUtf8(""))
- self.inboxSearchOption.addItem(_fromUtf8(""))
- self.inboxSearchOption.setSizeAdjustPolicy(QtGui.QComboBox.AdjustToContents)
+ self.inboxSearchOption = QtWidgets.QComboBox(self.inbox)
+ self.inboxSearchOption.setObjectName("inboxSearchOption")
+ self.inboxSearchOption.addItem("")
+ self.inboxSearchOption.addItem("")
+ self.inboxSearchOption.addItem("")
+ self.inboxSearchOption.addItem("")
+ self.inboxSearchOption.addItem("")
+ self.inboxSearchOption.setSizeAdjustPolicy(QtWidgets.QComboBox.AdjustToContents)
self.inboxSearchOption.setCurrentIndex(3)
self.horizontalSplitterSearch.addWidget(self.inboxSearchOption)
self.horizontalSplitterSearch.handle(1).setEnabled(False)
@@ -118,21 +89,21 @@ class Ui_MainWindow(object):
self.horizontalSplitterSearch.setStretchFactor(1, 0)
self.verticalSplitter_7.addWidget(self.horizontalSplitterSearch)
self.tableWidgetInbox = settingsmixin.STableWidget(self.inbox)
- self.tableWidgetInbox.setEditTriggers(QtGui.QAbstractItemView.NoEditTriggers)
+ self.tableWidgetInbox.setEditTriggers(QtWidgets.QAbstractItemView.NoEditTriggers)
self.tableWidgetInbox.setAlternatingRowColors(True)
- self.tableWidgetInbox.setSelectionMode(QtGui.QAbstractItemView.ExtendedSelection)
- self.tableWidgetInbox.setSelectionBehavior(QtGui.QAbstractItemView.SelectRows)
+ self.tableWidgetInbox.setSelectionMode(QtWidgets.QAbstractItemView.ExtendedSelection)
+ self.tableWidgetInbox.setSelectionBehavior(QtWidgets.QAbstractItemView.SelectRows)
self.tableWidgetInbox.setWordWrap(False)
- self.tableWidgetInbox.setObjectName(_fromUtf8("tableWidgetInbox"))
+ self.tableWidgetInbox.setObjectName("tableWidgetInbox")
self.tableWidgetInbox.setColumnCount(4)
self.tableWidgetInbox.setRowCount(0)
- item = QtGui.QTableWidgetItem()
+ item = QtWidgets.QTableWidgetItem()
self.tableWidgetInbox.setHorizontalHeaderItem(0, item)
- item = QtGui.QTableWidgetItem()
+ item = QtWidgets.QTableWidgetItem()
self.tableWidgetInbox.setHorizontalHeaderItem(1, item)
- item = QtGui.QTableWidgetItem()
+ item = QtWidgets.QTableWidgetItem()
self.tableWidgetInbox.setHorizontalHeaderItem(2, item)
- item = QtGui.QTableWidgetItem()
+ item = QtWidgets.QTableWidgetItem()
self.tableWidgetInbox.setHorizontalHeaderItem(3, item)
self.tableWidgetInbox.horizontalHeader().setCascadingSectionResizes(True)
self.tableWidgetInbox.horizontalHeader().setDefaultSectionSize(200)
@@ -146,7 +117,7 @@ class Ui_MainWindow(object):
self.textEditInboxMessage = MessageView(self.inbox)
self.textEditInboxMessage.setBaseSize(QtCore.QSize(0, 500))
self.textEditInboxMessage.setReadOnly(True)
- self.textEditInboxMessage.setObjectName(_fromUtf8("textEditInboxMessage"))
+ self.textEditInboxMessage.setObjectName("textEditInboxMessage")
self.verticalSplitter_7.addWidget(self.textEditInboxMessage)
self.verticalSplitter_7.setStretchFactor(0, 0)
self.verticalSplitter_7.setStretchFactor(1, 1)
@@ -162,52 +133,51 @@ class Ui_MainWindow(object):
self.horizontalSplitter_3.setCollapsible(1, False)
self.gridLayout.addWidget(self.horizontalSplitter_3)
icon2 = QtGui.QIcon()
- icon2.addPixmap(QtGui.QPixmap(_fromUtf8(":/newPrefix/images/inbox.png")), QtGui.QIcon.Normal, QtGui.QIcon.Off)
- self.tabWidget.addTab(self.inbox, icon2, _fromUtf8(""))
- self.send = QtGui.QWidget()
- self.send.setObjectName(_fromUtf8("send"))
- self.gridLayout_7 = QtGui.QGridLayout(self.send)
- self.gridLayout_7.setObjectName(_fromUtf8("gridLayout_7"))
+ icon2.addPixmap(QtGui.QPixmap(":/newPrefix/images/inbox.png"), QtGui.QIcon.Normal, QtGui.QIcon.Off)
+ self.tabWidget.addTab(self.inbox, icon2, "")
+ self.send = QtWidgets.QWidget()
+ self.send.setObjectName("send")
+ self.gridLayout_7 = QtWidgets.QGridLayout(self.send)
+ self.gridLayout_7.setObjectName("gridLayout_7")
self.horizontalSplitter = settingsmixin.SSplitter()
- self.horizontalSplitter.setObjectName(_fromUtf8("horizontalSplitter"))
+ self.horizontalSplitter.setObjectName("horizontalSplitter")
self.verticalSplitter_2 = settingsmixin.SSplitter()
- self.verticalSplitter_2.setObjectName(_fromUtf8("verticalSplitter_2"))
+ self.verticalSplitter_2.setObjectName("verticalSplitter_2")
self.verticalSplitter_2.setOrientation(QtCore.Qt.Vertical)
self.tableWidgetAddressBook = settingsmixin.STableWidget(self.send)
self.tableWidgetAddressBook.setAlternatingRowColors(True)
- self.tableWidgetAddressBook.setSelectionMode(QtGui.QAbstractItemView.ExtendedSelection)
- self.tableWidgetAddressBook.setSelectionBehavior(QtGui.QAbstractItemView.SelectRows)
- self.tableWidgetAddressBook.setObjectName(_fromUtf8("tableWidgetAddressBook"))
+ self.tableWidgetAddressBook.setSelectionMode(QtWidgets.QAbstractItemView.ExtendedSelection)
+ self.tableWidgetAddressBook.setSelectionBehavior(QtWidgets.QAbstractItemView.SelectRows)
+ self.tableWidgetAddressBook.setObjectName("tableWidgetAddressBook")
self.tableWidgetAddressBook.setColumnCount(2)
self.tableWidgetAddressBook.setRowCount(0)
self.tableWidgetAddressBook.resize(200, self.tableWidgetAddressBook.height())
- item = QtGui.QTableWidgetItem()
+ item = QtWidgets.QTableWidgetItem()
icon3 = QtGui.QIcon()
- icon3.addPixmap(
- QtGui.QPixmap(_fromUtf8(":/newPrefix/images/addressbook.png")), QtGui.QIcon.Selected, QtGui.QIcon.Off
- )
+ icon3.addPixmap(QtGui.QPixmap(":/newPrefix/images/addressbook.png"), QtGui.QIcon.Selected, QtGui.QIcon.Off)
item.setIcon(icon3)
self.tableWidgetAddressBook.setHorizontalHeaderItem(0, item)
- item = QtGui.QTableWidgetItem()
+ item = QtWidgets.QTableWidgetItem()
self.tableWidgetAddressBook.setHorizontalHeaderItem(1, item)
self.tableWidgetAddressBook.horizontalHeader().setCascadingSectionResizes(True)
self.tableWidgetAddressBook.horizontalHeader().setDefaultSectionSize(200)
self.tableWidgetAddressBook.horizontalHeader().setHighlightSections(False)
self.tableWidgetAddressBook.horizontalHeader().setStretchLastSection(True)
self.tableWidgetAddressBook.verticalHeader().setVisible(False)
+ self.tableWidgetAddressBook.setWordWrap(False)
self.verticalSplitter_2.addWidget(self.tableWidgetAddressBook)
self.addressBookCompleter = AddressBookCompleter()
- self.addressBookCompleter.setCompletionMode(QtGui.QCompleter.PopupCompletion)
+ self.addressBookCompleter.setCompletionMode(QtWidgets.QCompleter.PopupCompletion)
self.addressBookCompleter.setCaseSensitivity(QtCore.Qt.CaseInsensitive)
- self.addressBookCompleterModel = QtGui.QStringListModel()
+ self.addressBookCompleterModel = QtCore.QStringListModel()
self.addressBookCompleter.setModel(self.addressBookCompleterModel)
- self.pushButtonAddAddressBook = QtGui.QPushButton(self.send)
- self.pushButtonAddAddressBook.setObjectName(_fromUtf8("pushButtonAddAddressBook"))
+ self.pushButtonAddAddressBook = QtWidgets.QPushButton(self.send)
+ self.pushButtonAddAddressBook.setObjectName("pushButtonAddAddressBook")
self.pushButtonAddAddressBook.resize(200, self.pushButtonAddAddressBook.height())
self.verticalSplitter_2.addWidget(self.pushButtonAddAddressBook)
- self.pushButtonFetchNamecoinID = QtGui.QPushButton(self.send)
+ self.pushButtonFetchNamecoinID = QtWidgets.QPushButton(self.send)
self.pushButtonFetchNamecoinID.resize(200, self.pushButtonFetchNamecoinID.height())
- self.pushButtonFetchNamecoinID.setObjectName(_fromUtf8("pushButtonFetchNamecoinID"))
+ self.pushButtonFetchNamecoinID.setObjectName("pushButtonFetchNamecoinID")
self.verticalSplitter_2.addWidget(self.pushButtonFetchNamecoinID)
self.verticalSplitter_2.setStretchFactor(0, 1)
self.verticalSplitter_2.setStretchFactor(1, 0)
@@ -219,45 +189,45 @@ class Ui_MainWindow(object):
self.verticalSplitter_2.handle(2).setEnabled(False)
self.horizontalSplitter.addWidget(self.verticalSplitter_2)
self.verticalSplitter = settingsmixin.SSplitter()
- self.verticalSplitter.setObjectName(_fromUtf8("verticalSplitter"))
+ self.verticalSplitter.setObjectName("verticalSplitter")
self.verticalSplitter.setOrientation(QtCore.Qt.Vertical)
- self.tabWidgetSend = QtGui.QTabWidget(self.send)
- self.tabWidgetSend.setObjectName(_fromUtf8("tabWidgetSend"))
- self.sendDirect = QtGui.QWidget()
- self.sendDirect.setObjectName(_fromUtf8("sendDirect"))
- self.gridLayout_8 = QtGui.QGridLayout(self.sendDirect)
- self.gridLayout_8.setObjectName(_fromUtf8("gridLayout_8"))
+ self.tabWidgetSend = QtWidgets.QTabWidget(self.send)
+ self.tabWidgetSend.setObjectName("tabWidgetSend")
+ self.sendDirect = QtWidgets.QWidget()
+ self.sendDirect.setObjectName("sendDirect")
+ self.gridLayout_8 = QtWidgets.QGridLayout(self.sendDirect)
+ self.gridLayout_8.setObjectName("gridLayout_8")
self.verticalSplitter_5 = settingsmixin.SSplitter()
- self.verticalSplitter_5.setObjectName(_fromUtf8("verticalSplitter_5"))
+ self.verticalSplitter_5.setObjectName("verticalSplitter_5")
self.verticalSplitter_5.setOrientation(QtCore.Qt.Vertical)
- self.gridLayout_2 = QtGui.QGridLayout()
- self.gridLayout_2.setObjectName(_fromUtf8("gridLayout_2"))
- self.label_3 = QtGui.QLabel(self.sendDirect)
- self.label_3.setObjectName(_fromUtf8("label_3"))
+ self.gridLayout_2 = QtWidgets.QGridLayout()
+ self.gridLayout_2.setObjectName("gridLayout_2")
+ self.label_3 = QtWidgets.QLabel(self.sendDirect)
+ self.label_3.setObjectName("label_3")
self.gridLayout_2.addWidget(self.label_3, 2, 0, 1, 1)
- self.label_2 = QtGui.QLabel(self.sendDirect)
- self.label_2.setObjectName(_fromUtf8("label_2"))
+ self.label_2 = QtWidgets.QLabel(self.sendDirect)
+ self.label_2.setObjectName("label_2")
self.gridLayout_2.addWidget(self.label_2, 0, 0, 1, 1)
- self.lineEditSubject = QtGui.QLineEdit(self.sendDirect)
- self.lineEditSubject.setText(_fromUtf8(""))
- self.lineEditSubject.setObjectName(_fromUtf8("lineEditSubject"))
+ self.lineEditSubject = QtWidgets.QLineEdit(self.sendDirect)
+ self.lineEditSubject.setText("")
+ self.lineEditSubject.setObjectName("lineEditSubject")
self.gridLayout_2.addWidget(self.lineEditSubject, 2, 1, 1, 1)
- self.label = QtGui.QLabel(self.sendDirect)
- self.label.setObjectName(_fromUtf8("label"))
+ self.label = QtWidgets.QLabel(self.sendDirect)
+ self.label.setObjectName("label")
self.gridLayout_2.addWidget(self.label, 1, 0, 1, 1)
- self.comboBoxSendFrom = QtGui.QComboBox(self.sendDirect)
+ self.comboBoxSendFrom = QtWidgets.QComboBox(self.sendDirect)
self.comboBoxSendFrom.setMinimumSize(QtCore.QSize(300, 0))
- self.comboBoxSendFrom.setObjectName(_fromUtf8("comboBoxSendFrom"))
+ self.comboBoxSendFrom.setObjectName("comboBoxSendFrom")
self.gridLayout_2.addWidget(self.comboBoxSendFrom, 0, 1, 1, 1)
- self.lineEditTo = QtGui.QLineEdit(self.sendDirect)
- self.lineEditTo.setObjectName(_fromUtf8("lineEditTo"))
+ self.lineEditTo = QtWidgets.QLineEdit(self.sendDirect)
+ self.lineEditTo.setObjectName("lineEditTo")
self.gridLayout_2.addWidget(self.lineEditTo, 1, 1, 1, 1)
self.lineEditTo.setCompleter(self.addressBookCompleter)
- self.gridLayout_2_Widget = QtGui.QWidget()
+ self.gridLayout_2_Widget = QtWidgets.QWidget()
self.gridLayout_2_Widget.setLayout(self.gridLayout_2)
self.verticalSplitter_5.addWidget(self.gridLayout_2_Widget)
self.textEditMessage = MessageCompose(self.sendDirect)
- self.textEditMessage.setObjectName(_fromUtf8("textEditMessage"))
+ self.textEditMessage.setObjectName("textEditMessage")
self.verticalSplitter_5.addWidget(self.textEditMessage)
self.verticalSplitter_5.setStretchFactor(0, 0)
self.verticalSplitter_5.setStretchFactor(1, 1)
@@ -265,35 +235,35 @@ class Ui_MainWindow(object):
self.verticalSplitter_5.setCollapsible(1, False)
self.verticalSplitter_5.handle(1).setEnabled(False)
self.gridLayout_8.addWidget(self.verticalSplitter_5, 0, 0, 1, 1)
- self.tabWidgetSend.addTab(self.sendDirect, _fromUtf8(""))
- self.sendBroadcast = QtGui.QWidget()
- self.sendBroadcast.setObjectName(_fromUtf8("sendBroadcast"))
- self.gridLayout_9 = QtGui.QGridLayout(self.sendBroadcast)
- self.gridLayout_9.setObjectName(_fromUtf8("gridLayout_9"))
+ self.tabWidgetSend.addTab(self.sendDirect, "")
+ self.sendBroadcast = QtWidgets.QWidget()
+ self.sendBroadcast.setObjectName("sendBroadcast")
+ self.gridLayout_9 = QtWidgets.QGridLayout(self.sendBroadcast)
+ self.gridLayout_9.setObjectName("gridLayout_9")
self.verticalSplitter_6 = settingsmixin.SSplitter()
- self.verticalSplitter_6.setObjectName(_fromUtf8("verticalSplitter_6"))
+ self.verticalSplitter_6.setObjectName("verticalSplitter_6")
self.verticalSplitter_6.setOrientation(QtCore.Qt.Vertical)
- self.gridLayout_5 = QtGui.QGridLayout()
- self.gridLayout_5.setObjectName(_fromUtf8("gridLayout_5"))
- self.label_8 = QtGui.QLabel(self.sendBroadcast)
- self.label_8.setObjectName(_fromUtf8("label_8"))
+ self.gridLayout_5 = QtWidgets.QGridLayout()
+ self.gridLayout_5.setObjectName("gridLayout_5")
+ self.label_8 = QtWidgets.QLabel(self.sendBroadcast)
+ self.label_8.setObjectName("label_8")
self.gridLayout_5.addWidget(self.label_8, 0, 0, 1, 1)
- self.lineEditSubjectBroadcast = QtGui.QLineEdit(self.sendBroadcast)
- self.lineEditSubjectBroadcast.setText(_fromUtf8(""))
- self.lineEditSubjectBroadcast.setObjectName(_fromUtf8("lineEditSubjectBroadcast"))
+ self.lineEditSubjectBroadcast = QtWidgets.QLineEdit(self.sendBroadcast)
+ self.lineEditSubjectBroadcast.setText("")
+ self.lineEditSubjectBroadcast.setObjectName("lineEditSubjectBroadcast")
self.gridLayout_5.addWidget(self.lineEditSubjectBroadcast, 1, 1, 1, 1)
- self.label_7 = QtGui.QLabel(self.sendBroadcast)
- self.label_7.setObjectName(_fromUtf8("label_7"))
+ self.label_7 = QtWidgets.QLabel(self.sendBroadcast)
+ self.label_7.setObjectName("label_7")
self.gridLayout_5.addWidget(self.label_7, 1, 0, 1, 1)
- self.comboBoxSendFromBroadcast = QtGui.QComboBox(self.sendBroadcast)
+ self.comboBoxSendFromBroadcast = QtWidgets.QComboBox(self.sendBroadcast)
self.comboBoxSendFromBroadcast.setMinimumSize(QtCore.QSize(300, 0))
- self.comboBoxSendFromBroadcast.setObjectName(_fromUtf8("comboBoxSendFromBroadcast"))
+ self.comboBoxSendFromBroadcast.setObjectName("comboBoxSendFromBroadcast")
self.gridLayout_5.addWidget(self.comboBoxSendFromBroadcast, 0, 1, 1, 1)
- self.gridLayout_5_Widget = QtGui.QWidget()
+ self.gridLayout_5_Widget = QtWidgets.QWidget()
self.gridLayout_5_Widget.setLayout(self.gridLayout_5)
self.verticalSplitter_6.addWidget(self.gridLayout_5_Widget)
self.textEditMessageBroadcast = MessageCompose(self.sendBroadcast)
- self.textEditMessageBroadcast.setObjectName(_fromUtf8("textEditMessageBroadcast"))
+ self.textEditMessageBroadcast.setObjectName("textEditMessageBroadcast")
self.verticalSplitter_6.addWidget(self.textEditMessageBroadcast)
self.verticalSplitter_6.setStretchFactor(0, 0)
self.verticalSplitter_6.setStretchFactor(1, 1)
@@ -301,15 +271,15 @@ class Ui_MainWindow(object):
self.verticalSplitter_6.setCollapsible(1, False)
self.verticalSplitter_6.handle(1).setEnabled(False)
self.gridLayout_9.addWidget(self.verticalSplitter_6, 0, 0, 1, 1)
- self.tabWidgetSend.addTab(self.sendBroadcast, _fromUtf8(""))
+ self.tabWidgetSend.addTab(self.sendBroadcast, "")
self.verticalSplitter.addWidget(self.tabWidgetSend)
- self.tTLContainer = QtGui.QWidget()
- self.tTLContainer.setSizePolicy(QtGui.QSizePolicy.MinimumExpanding, QtGui.QSizePolicy.Fixed)
- self.horizontalLayout_5 = QtGui.QHBoxLayout()
+ self.tTLContainer = QtWidgets.QWidget()
+ self.tTLContainer.setSizePolicy(QtWidgets.QSizePolicy.MinimumExpanding, QtWidgets.QSizePolicy.Fixed)
+ self.horizontalLayout_5 = QtWidgets.QHBoxLayout()
self.tTLContainer.setLayout(self.horizontalLayout_5)
- self.horizontalLayout_5.setObjectName(_fromUtf8("horizontalLayout_5"))
- self.pushButtonTTL = QtGui.QPushButton(self.send)
- sizePolicy = QtGui.QSizePolicy(QtGui.QSizePolicy.MinimumExpanding, QtGui.QSizePolicy.Fixed)
+ self.horizontalLayout_5.setObjectName("horizontalLayout_5")
+ self.pushButtonTTL = QtWidgets.QPushButton(self.send)
+ sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.MinimumExpanding, QtWidgets.QSizePolicy.Fixed)
sizePolicy.setHorizontalStretch(0)
sizePolicy.setVerticalStretch(0)
sizePolicy.setHeightForWidth(self.pushButtonTTL.sizePolicy().hasHeightForWidth())
@@ -329,29 +299,29 @@ class Ui_MainWindow(object):
font.setUnderline(True)
self.pushButtonTTL.setFont(font)
self.pushButtonTTL.setFlat(True)
- self.pushButtonTTL.setObjectName(_fromUtf8("pushButtonTTL"))
+ self.pushButtonTTL.setObjectName("pushButtonTTL")
self.horizontalLayout_5.addWidget(self.pushButtonTTL, 0, QtCore.Qt.AlignRight)
- self.horizontalSliderTTL = QtGui.QSlider(self.send)
+ self.horizontalSliderTTL = QtWidgets.QSlider(self.send)
self.horizontalSliderTTL.setMinimumSize(QtCore.QSize(70, 0))
self.horizontalSliderTTL.setOrientation(QtCore.Qt.Horizontal)
self.horizontalSliderTTL.setInvertedAppearance(False)
self.horizontalSliderTTL.setInvertedControls(False)
- self.horizontalSliderTTL.setObjectName(_fromUtf8("horizontalSliderTTL"))
+ self.horizontalSliderTTL.setObjectName("horizontalSliderTTL")
self.horizontalLayout_5.addWidget(self.horizontalSliderTTL, 0, QtCore.Qt.AlignLeft)
- self.labelHumanFriendlyTTLDescription = QtGui.QLabel(self.send)
- sizePolicy = QtGui.QSizePolicy(QtGui.QSizePolicy.MinimumExpanding, QtGui.QSizePolicy.Fixed)
+ self.labelHumanFriendlyTTLDescription = QtWidgets.QLabel(self.send)
+ sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.MinimumExpanding, QtWidgets.QSizePolicy.Fixed)
sizePolicy.setHorizontalStretch(0)
sizePolicy.setVerticalStretch(0)
sizePolicy.setHeightForWidth(self.labelHumanFriendlyTTLDescription.sizePolicy().hasHeightForWidth())
self.labelHumanFriendlyTTLDescription.setSizePolicy(sizePolicy)
self.labelHumanFriendlyTTLDescription.setMinimumSize(QtCore.QSize(45, 0))
- self.labelHumanFriendlyTTLDescription.setObjectName(_fromUtf8("labelHumanFriendlyTTLDescription"))
+ self.labelHumanFriendlyTTLDescription.setObjectName("labelHumanFriendlyTTLDescription")
self.horizontalLayout_5.addWidget(self.labelHumanFriendlyTTLDescription, 1, QtCore.Qt.AlignLeft)
- self.pushButtonClear = QtGui.QPushButton(self.send)
- self.pushButtonClear.setObjectName(_fromUtf8("pushButtonClear"))
+ self.pushButtonClear = QtWidgets.QPushButton(self.send)
+ self.pushButtonClear.setObjectName("pushButtonClear")
self.horizontalLayout_5.addWidget(self.pushButtonClear, 0, QtCore.Qt.AlignRight)
- self.pushButtonSend = QtGui.QPushButton(self.send)
- self.pushButtonSend.setObjectName(_fromUtf8("pushButtonSend"))
+ self.pushButtonSend = QtWidgets.QPushButton(self.send)
+ self.pushButtonSend.setObjectName("pushButtonSend")
self.horizontalLayout_5.addWidget(self.pushButtonSend, 0, QtCore.Qt.AlignRight)
self.horizontalSliderTTL.setMaximumSize(QtCore.QSize(105, self.pushButtonSend.height()))
self.verticalSplitter.addWidget(self.tTLContainer)
@@ -368,31 +338,29 @@ class Ui_MainWindow(object):
self.horizontalSplitter.setCollapsible(1, False)
self.gridLayout_7.addWidget(self.horizontalSplitter, 0, 0, 1, 1)
icon4 = QtGui.QIcon()
- icon4.addPixmap(QtGui.QPixmap(_fromUtf8(":/newPrefix/images/send.png")), QtGui.QIcon.Normal, QtGui.QIcon.Off)
- self.tabWidget.addTab(self.send, icon4, _fromUtf8(""))
- self.subscriptions = QtGui.QWidget()
- self.subscriptions.setObjectName(_fromUtf8("subscriptions"))
- self.gridLayout_3 = QtGui.QGridLayout(self.subscriptions)
- self.gridLayout_3.setObjectName(_fromUtf8("gridLayout_3"))
+ icon4.addPixmap(QtGui.QPixmap(":/newPrefix/images/send.png"), QtGui.QIcon.Normal, QtGui.QIcon.Off)
+ self.tabWidget.addTab(self.send, icon4, "")
+ self.subscriptions = QtWidgets.QWidget()
+ self.subscriptions.setObjectName("subscriptions")
+ self.gridLayout_3 = QtWidgets.QGridLayout(self.subscriptions)
+ self.gridLayout_3.setObjectName("gridLayout_3")
self.horizontalSplitter_4 = settingsmixin.SSplitter()
- self.horizontalSplitter_4.setObjectName(_fromUtf8("horizontalSplitter_4"))
+ self.horizontalSplitter_4.setObjectName("horizontalSplitter_4")
self.verticalSplitter_3 = settingsmixin.SSplitter()
- self.verticalSplitter_3.setObjectName(_fromUtf8("verticalSplitter_3"))
+ self.verticalSplitter_3.setObjectName("verticalSplitter_3")
self.verticalSplitter_3.setOrientation(QtCore.Qt.Vertical)
self.treeWidgetSubscriptions = settingsmixin.STreeWidget(self.subscriptions)
self.treeWidgetSubscriptions.setAlternatingRowColors(True)
- self.treeWidgetSubscriptions.setSelectionMode(QtGui.QAbstractItemView.SingleSelection)
- self.treeWidgetSubscriptions.setSelectionBehavior(QtGui.QAbstractItemView.SelectRows)
- self.treeWidgetSubscriptions.setObjectName(_fromUtf8("treeWidgetSubscriptions"))
+ self.treeWidgetSubscriptions.setSelectionMode(QtWidgets.QAbstractItemView.SingleSelection)
+ self.treeWidgetSubscriptions.setSelectionBehavior(QtWidgets.QAbstractItemView.SelectRows)
+ self.treeWidgetSubscriptions.setObjectName("treeWidgetSubscriptions")
self.treeWidgetSubscriptions.resize(200, self.treeWidgetSubscriptions.height())
icon5 = QtGui.QIcon()
- icon5.addPixmap(
- QtGui.QPixmap(_fromUtf8(":/newPrefix/images/subscriptions.png")), QtGui.QIcon.Selected, QtGui.QIcon.Off
- )
+ icon5.addPixmap(QtGui.QPixmap(":/newPrefix/images/subscriptions.png"), QtGui.QIcon.Selected, QtGui.QIcon.Off)
self.treeWidgetSubscriptions.headerItem().setIcon(0, icon5)
self.verticalSplitter_3.addWidget(self.treeWidgetSubscriptions)
- self.pushButtonAddSubscription = QtGui.QPushButton(self.subscriptions)
- self.pushButtonAddSubscription.setObjectName(_fromUtf8("pushButtonAddSubscription"))
+ self.pushButtonAddSubscription = QtWidgets.QPushButton(self.subscriptions)
+ self.pushButtonAddSubscription.setObjectName("pushButtonAddSubscription")
self.pushButtonAddSubscription.resize(200, self.pushButtonAddSubscription.height())
self.verticalSplitter_3.addWidget(self.pushButtonAddSubscription)
self.verticalSplitter_3.setStretchFactor(0, 1)
@@ -402,20 +370,20 @@ class Ui_MainWindow(object):
self.verticalSplitter_3.handle(1).setEnabled(False)
self.horizontalSplitter_4.addWidget(self.verticalSplitter_3)
self.verticalSplitter_4 = settingsmixin.SSplitter()
- self.verticalSplitter_4.setObjectName(_fromUtf8("verticalSplitter_4"))
+ self.verticalSplitter_4.setObjectName("verticalSplitter_4")
self.verticalSplitter_4.setOrientation(QtCore.Qt.Vertical)
- self.horizontalSplitter_2 = QtGui.QSplitter()
- self.horizontalSplitter_2.setObjectName(_fromUtf8("horizontalSplitter_2"))
- self.inboxSearchLineEditSubscriptions = QtGui.QLineEdit(self.subscriptions)
- self.inboxSearchLineEditSubscriptions.setObjectName(_fromUtf8("inboxSearchLineEditSubscriptions"))
+ self.horizontalSplitter_2 = QtWidgets.QSplitter()
+ self.horizontalSplitter_2.setObjectName("horizontalSplitter_2")
+ self.inboxSearchLineEditSubscriptions = QtWidgets.QLineEdit(self.subscriptions)
+ self.inboxSearchLineEditSubscriptions.setObjectName("inboxSearchLineEditSubscriptions")
self.horizontalSplitter_2.addWidget(self.inboxSearchLineEditSubscriptions)
- self.inboxSearchOptionSubscriptions = QtGui.QComboBox(self.subscriptions)
- self.inboxSearchOptionSubscriptions.setObjectName(_fromUtf8("inboxSearchOptionSubscriptions"))
- self.inboxSearchOptionSubscriptions.addItem(_fromUtf8(""))
- self.inboxSearchOptionSubscriptions.addItem(_fromUtf8(""))
- self.inboxSearchOptionSubscriptions.addItem(_fromUtf8(""))
- self.inboxSearchOptionSubscriptions.addItem(_fromUtf8(""))
- self.inboxSearchOptionSubscriptions.setSizeAdjustPolicy(QtGui.QComboBox.AdjustToContents)
+ self.inboxSearchOptionSubscriptions = QtWidgets.QComboBox(self.subscriptions)
+ self.inboxSearchOptionSubscriptions.setObjectName("inboxSearchOptionSubscriptions")
+ self.inboxSearchOptionSubscriptions.addItem("")
+ self.inboxSearchOptionSubscriptions.addItem("")
+ self.inboxSearchOptionSubscriptions.addItem("")
+ self.inboxSearchOptionSubscriptions.addItem("")
+ self.inboxSearchOptionSubscriptions.setSizeAdjustPolicy(QtWidgets.QComboBox.AdjustToContents)
self.inboxSearchOptionSubscriptions.setCurrentIndex(2)
self.horizontalSplitter_2.addWidget(self.inboxSearchOptionSubscriptions)
self.horizontalSplitter_2.handle(1).setEnabled(False)
@@ -423,21 +391,21 @@ class Ui_MainWindow(object):
self.horizontalSplitter_2.setStretchFactor(1, 0)
self.verticalSplitter_4.addWidget(self.horizontalSplitter_2)
self.tableWidgetInboxSubscriptions = settingsmixin.STableWidget(self.subscriptions)
- self.tableWidgetInboxSubscriptions.setEditTriggers(QtGui.QAbstractItemView.NoEditTriggers)
+ self.tableWidgetInboxSubscriptions.setEditTriggers(QtWidgets.QAbstractItemView.NoEditTriggers)
self.tableWidgetInboxSubscriptions.setAlternatingRowColors(True)
- self.tableWidgetInboxSubscriptions.setSelectionMode(QtGui.QAbstractItemView.ExtendedSelection)
- self.tableWidgetInboxSubscriptions.setSelectionBehavior(QtGui.QAbstractItemView.SelectRows)
+ self.tableWidgetInboxSubscriptions.setSelectionMode(QtWidgets.QAbstractItemView.ExtendedSelection)
+ self.tableWidgetInboxSubscriptions.setSelectionBehavior(QtWidgets.QAbstractItemView.SelectRows)
self.tableWidgetInboxSubscriptions.setWordWrap(False)
- self.tableWidgetInboxSubscriptions.setObjectName(_fromUtf8("tableWidgetInboxSubscriptions"))
+ self.tableWidgetInboxSubscriptions.setObjectName("tableWidgetInboxSubscriptions")
self.tableWidgetInboxSubscriptions.setColumnCount(4)
self.tableWidgetInboxSubscriptions.setRowCount(0)
- item = QtGui.QTableWidgetItem()
+ item = QtWidgets.QTableWidgetItem()
self.tableWidgetInboxSubscriptions.setHorizontalHeaderItem(0, item)
- item = QtGui.QTableWidgetItem()
+ item = QtWidgets.QTableWidgetItem()
self.tableWidgetInboxSubscriptions.setHorizontalHeaderItem(1, item)
- item = QtGui.QTableWidgetItem()
+ item = QtWidgets.QTableWidgetItem()
self.tableWidgetInboxSubscriptions.setHorizontalHeaderItem(2, item)
- item = QtGui.QTableWidgetItem()
+ item = QtWidgets.QTableWidgetItem()
self.tableWidgetInboxSubscriptions.setHorizontalHeaderItem(3, item)
self.tableWidgetInboxSubscriptions.horizontalHeader().setCascadingSectionResizes(True)
self.tableWidgetInboxSubscriptions.horizontalHeader().setDefaultSectionSize(200)
@@ -451,7 +419,7 @@ class Ui_MainWindow(object):
self.textEditInboxMessageSubscriptions = MessageView(self.subscriptions)
self.textEditInboxMessageSubscriptions.setBaseSize(QtCore.QSize(0, 500))
self.textEditInboxMessageSubscriptions.setReadOnly(True)
- self.textEditInboxMessageSubscriptions.setObjectName(_fromUtf8("textEditInboxMessageSubscriptions"))
+ self.textEditInboxMessageSubscriptions.setObjectName("textEditInboxMessageSubscriptions")
self.verticalSplitter_4.addWidget(self.textEditInboxMessageSubscriptions)
self.verticalSplitter_4.setStretchFactor(0, 0)
self.verticalSplitter_4.setStretchFactor(1, 1)
@@ -467,35 +435,31 @@ class Ui_MainWindow(object):
self.horizontalSplitter_4.setCollapsible(1, False)
self.gridLayout_3.addWidget(self.horizontalSplitter_4, 0, 0, 1, 1)
icon6 = QtGui.QIcon()
- icon6.addPixmap(
- QtGui.QPixmap(_fromUtf8(":/newPrefix/images/subscriptions.png")), QtGui.QIcon.Normal, QtGui.QIcon.Off
- )
- self.tabWidget.addTab(self.subscriptions, icon6, _fromUtf8(""))
- self.chans = QtGui.QWidget()
- self.chans.setObjectName(_fromUtf8("chans"))
- self.gridLayout_4 = QtGui.QGridLayout(self.chans)
- self.gridLayout_4.setObjectName(_fromUtf8("gridLayout_4"))
+ icon6.addPixmap(QtGui.QPixmap(":/newPrefix/images/subscriptions.png"), QtGui.QIcon.Normal, QtGui.QIcon.Off)
+ self.tabWidget.addTab(self.subscriptions, icon6, "")
+ self.chans = QtWidgets.QWidget()
+ self.chans.setObjectName("chans")
+ self.gridLayout_4 = QtWidgets.QGridLayout(self.chans)
+ self.gridLayout_4.setObjectName("gridLayout_4")
self.horizontalSplitter_7 = settingsmixin.SSplitter()
- self.horizontalSplitter_7.setObjectName(_fromUtf8("horizontalSplitter_7"))
+ self.horizontalSplitter_7.setObjectName("horizontalSplitter_7")
self.verticalSplitter_17 = settingsmixin.SSplitter()
- self.verticalSplitter_17.setObjectName(_fromUtf8("verticalSplitter_17"))
+ self.verticalSplitter_17.setObjectName("verticalSplitter_17")
self.verticalSplitter_17.setOrientation(QtCore.Qt.Vertical)
self.treeWidgetChans = settingsmixin.STreeWidget(self.chans)
- self.treeWidgetChans.setFrameShadow(QtGui.QFrame.Sunken)
+ self.treeWidgetChans.setFrameShadow(QtWidgets.QFrame.Sunken)
self.treeWidgetChans.setLineWidth(1)
self.treeWidgetChans.setAlternatingRowColors(True)
- self.treeWidgetChans.setSelectionMode(QtGui.QAbstractItemView.SingleSelection)
- self.treeWidgetChans.setSelectionBehavior(QtGui.QAbstractItemView.SelectRows)
- self.treeWidgetChans.setObjectName(_fromUtf8("treeWidgetChans"))
+ self.treeWidgetChans.setSelectionMode(QtWidgets.QAbstractItemView.SingleSelection)
+ self.treeWidgetChans.setSelectionBehavior(QtWidgets.QAbstractItemView.SelectRows)
+ self.treeWidgetChans.setObjectName("treeWidgetChans")
self.treeWidgetChans.resize(200, self.treeWidgetChans.height())
icon7 = QtGui.QIcon()
- icon7.addPixmap(
- QtGui.QPixmap(_fromUtf8(":/newPrefix/images/can-icon-16px.png")), QtGui.QIcon.Selected, QtGui.QIcon.Off
- )
+ icon7.addPixmap(QtGui.QPixmap(":/newPrefix/images/can-icon-16px.png"), QtGui.QIcon.Selected, QtGui.QIcon.Off)
self.treeWidgetChans.headerItem().setIcon(0, icon7)
self.verticalSplitter_17.addWidget(self.treeWidgetChans)
- self.pushButtonAddChan = QtGui.QPushButton(self.chans)
- self.pushButtonAddChan.setObjectName(_fromUtf8("pushButtonAddChan"))
+ self.pushButtonAddChan = QtWidgets.QPushButton(self.chans)
+ self.pushButtonAddChan.setObjectName("pushButtonAddChan")
self.pushButtonAddChan.resize(200, self.pushButtonAddChan.height())
self.verticalSplitter_17.addWidget(self.pushButtonAddChan)
self.verticalSplitter_17.setStretchFactor(0, 1)
@@ -505,21 +469,21 @@ class Ui_MainWindow(object):
self.verticalSplitter_17.handle(1).setEnabled(False)
self.horizontalSplitter_7.addWidget(self.verticalSplitter_17)
self.verticalSplitter_8 = settingsmixin.SSplitter()
- self.verticalSplitter_8.setObjectName(_fromUtf8("verticalSplitter_8"))
+ self.verticalSplitter_8.setObjectName("verticalSplitter_8")
self.verticalSplitter_8.setOrientation(QtCore.Qt.Vertical)
- self.horizontalSplitter_6 = QtGui.QSplitter()
- self.horizontalSplitter_6.setObjectName(_fromUtf8("horizontalSplitter_6"))
- self.inboxSearchLineEditChans = QtGui.QLineEdit(self.chans)
- self.inboxSearchLineEditChans.setObjectName(_fromUtf8("inboxSearchLineEditChans"))
+ self.horizontalSplitter_6 = QtWidgets.QSplitter()
+ self.horizontalSplitter_6.setObjectName("horizontalSplitter_6")
+ self.inboxSearchLineEditChans = QtWidgets.QLineEdit(self.chans)
+ self.inboxSearchLineEditChans.setObjectName("inboxSearchLineEditChans")
self.horizontalSplitter_6.addWidget(self.inboxSearchLineEditChans)
- self.inboxSearchOptionChans = QtGui.QComboBox(self.chans)
- self.inboxSearchOptionChans.setObjectName(_fromUtf8("inboxSearchOptionChans"))
- self.inboxSearchOptionChans.addItem(_fromUtf8(""))
- self.inboxSearchOptionChans.addItem(_fromUtf8(""))
- self.inboxSearchOptionChans.addItem(_fromUtf8(""))
- self.inboxSearchOptionChans.addItem(_fromUtf8(""))
- self.inboxSearchOptionChans.addItem(_fromUtf8(""))
- self.inboxSearchOptionChans.setSizeAdjustPolicy(QtGui.QComboBox.AdjustToContents)
+ self.inboxSearchOptionChans = QtWidgets.QComboBox(self.chans)
+ self.inboxSearchOptionChans.setObjectName("inboxSearchOptionChans")
+ self.inboxSearchOptionChans.addItem("")
+ self.inboxSearchOptionChans.addItem("")
+ self.inboxSearchOptionChans.addItem("")
+ self.inboxSearchOptionChans.addItem("")
+ self.inboxSearchOptionChans.addItem("")
+ self.inboxSearchOptionChans.setSizeAdjustPolicy(QtWidgets.QComboBox.AdjustToContents)
self.inboxSearchOptionChans.setCurrentIndex(3)
self.horizontalSplitter_6.addWidget(self.inboxSearchOptionChans)
self.horizontalSplitter_6.handle(1).setEnabled(False)
@@ -527,21 +491,21 @@ class Ui_MainWindow(object):
self.horizontalSplitter_6.setStretchFactor(1, 0)
self.verticalSplitter_8.addWidget(self.horizontalSplitter_6)
self.tableWidgetInboxChans = settingsmixin.STableWidget(self.chans)
- self.tableWidgetInboxChans.setEditTriggers(QtGui.QAbstractItemView.NoEditTriggers)
+ self.tableWidgetInboxChans.setEditTriggers(QtWidgets.QAbstractItemView.NoEditTriggers)
self.tableWidgetInboxChans.setAlternatingRowColors(True)
- self.tableWidgetInboxChans.setSelectionMode(QtGui.QAbstractItemView.ExtendedSelection)
- self.tableWidgetInboxChans.setSelectionBehavior(QtGui.QAbstractItemView.SelectRows)
+ self.tableWidgetInboxChans.setSelectionMode(QtWidgets.QAbstractItemView.ExtendedSelection)
+ self.tableWidgetInboxChans.setSelectionBehavior(QtWidgets.QAbstractItemView.SelectRows)
self.tableWidgetInboxChans.setWordWrap(False)
- self.tableWidgetInboxChans.setObjectName(_fromUtf8("tableWidgetInboxChans"))
+ self.tableWidgetInboxChans.setObjectName("tableWidgetInboxChans")
self.tableWidgetInboxChans.setColumnCount(4)
self.tableWidgetInboxChans.setRowCount(0)
- item = QtGui.QTableWidgetItem()
+ item = QtWidgets.QTableWidgetItem()
self.tableWidgetInboxChans.setHorizontalHeaderItem(0, item)
- item = QtGui.QTableWidgetItem()
+ item = QtWidgets.QTableWidgetItem()
self.tableWidgetInboxChans.setHorizontalHeaderItem(1, item)
- item = QtGui.QTableWidgetItem()
+ item = QtWidgets.QTableWidgetItem()
self.tableWidgetInboxChans.setHorizontalHeaderItem(2, item)
- item = QtGui.QTableWidgetItem()
+ item = QtWidgets.QTableWidgetItem()
self.tableWidgetInboxChans.setHorizontalHeaderItem(3, item)
self.tableWidgetInboxChans.horizontalHeader().setCascadingSectionResizes(True)
self.tableWidgetInboxChans.horizontalHeader().setDefaultSectionSize(200)
@@ -555,7 +519,7 @@ class Ui_MainWindow(object):
self.textEditInboxMessageChans = MessageView(self.chans)
self.textEditInboxMessageChans.setBaseSize(QtCore.QSize(0, 500))
self.textEditInboxMessageChans.setReadOnly(True)
- self.textEditInboxMessageChans.setObjectName(_fromUtf8("textEditInboxMessageChans"))
+ self.textEditInboxMessageChans.setObjectName("textEditInboxMessageChans")
self.verticalSplitter_8.addWidget(self.textEditInboxMessageChans)
self.verticalSplitter_8.setStretchFactor(0, 0)
self.verticalSplitter_8.setStretchFactor(1, 1)
@@ -571,10 +535,8 @@ class Ui_MainWindow(object):
self.horizontalSplitter_7.setCollapsible(1, False)
self.gridLayout_4.addWidget(self.horizontalSplitter_7, 0, 0, 1, 1)
icon8 = QtGui.QIcon()
- icon8.addPixmap(
- QtGui.QPixmap(_fromUtf8(":/newPrefix/images/can-icon-16px.png")), QtGui.QIcon.Normal, QtGui.QIcon.Off
- )
- self.tabWidget.addTab(self.chans, icon8, _fromUtf8(""))
+ icon8.addPixmap(QtGui.QPixmap(":/newPrefix/images/can-icon-16px.png"), QtGui.QIcon.Normal, QtGui.QIcon.Off)
+ self.tabWidget.addTab(self.chans, icon8, "")
self.blackwhitelist = Blacklist()
self.tabWidget.addTab(self.blackwhitelist, QtGui.QIcon(":/newPrefix/images/blacklist.png"), "")
# Initialize the Blacklist or Whitelist
@@ -586,62 +548,62 @@ class Ui_MainWindow(object):
self.tabWidget.addTab(self.networkstatus, QtGui.QIcon(":/newPrefix/images/networkstatus.png"), "")
self.gridLayout_10.addWidget(self.tabWidget, 0, 0, 1, 1)
MainWindow.setCentralWidget(self.centralwidget)
- self.menubar = QtGui.QMenuBar(MainWindow)
+ self.menubar = QtWidgets.QMenuBar(MainWindow)
self.menubar.setGeometry(QtCore.QRect(0, 0, 885, 27))
- self.menubar.setObjectName(_fromUtf8("menubar"))
- self.menuFile = QtGui.QMenu(self.menubar)
- self.menuFile.setObjectName(_fromUtf8("menuFile"))
- self.menuSettings = QtGui.QMenu(self.menubar)
- self.menuSettings.setObjectName(_fromUtf8("menuSettings"))
- self.menuHelp = QtGui.QMenu(self.menubar)
- self.menuHelp.setObjectName(_fromUtf8("menuHelp"))
+ self.menubar.setObjectName("menubar")
+ self.menuFile = QtWidgets.QMenu(self.menubar)
+ self.menuFile.setObjectName("menuFile")
+ self.menuSettings = QtWidgets.QMenu(self.menubar)
+ self.menuSettings.setObjectName("menuSettings")
+ self.menuHelp = QtWidgets.QMenu(self.menubar)
+ self.menuHelp.setObjectName("menuHelp")
MainWindow.setMenuBar(self.menubar)
- self.statusbar = QtGui.QStatusBar(MainWindow)
+ self.statusbar = QtWidgets.QStatusBar(MainWindow)
self.statusbar.setMaximumSize(QtCore.QSize(16777215, 22))
- self.statusbar.setObjectName(_fromUtf8("statusbar"))
+ self.statusbar.setObjectName("statusbar")
MainWindow.setStatusBar(self.statusbar)
- self.actionImport_keys = QtGui.QAction(MainWindow)
- self.actionImport_keys.setObjectName(_fromUtf8("actionImport_keys"))
- self.actionManageKeys = QtGui.QAction(MainWindow)
+ self.actionImport_keys = QtWidgets.QAction(MainWindow)
+ self.actionImport_keys.setObjectName("actionImport_keys")
+ self.actionManageKeys = QtWidgets.QAction(MainWindow)
self.actionManageKeys.setCheckable(False)
self.actionManageKeys.setEnabled(True)
- icon = QtGui.QIcon.fromTheme(_fromUtf8("dialog-password"))
+ icon = QtGui.QIcon.fromTheme("dialog-password")
self.actionManageKeys.setIcon(icon)
- self.actionManageKeys.setObjectName(_fromUtf8("actionManageKeys"))
- self.actionNetworkSwitch = QtGui.QAction(MainWindow)
- self.actionNetworkSwitch.setObjectName(_fromUtf8("actionNetworkSwitch"))
- self.actionExit = QtGui.QAction(MainWindow)
- icon = QtGui.QIcon.fromTheme(_fromUtf8("application-exit"))
+ self.actionManageKeys.setObjectName("actionManageKeys")
+ self.actionNetworkSwitch = QtWidgets.QAction(MainWindow)
+ self.actionNetworkSwitch.setObjectName("actionNetworkSwitch")
+ self.actionExit = QtWidgets.QAction(MainWindow)
+ icon = QtGui.QIcon.fromTheme("application-exit")
self.actionExit.setIcon(icon)
- self.actionExit.setObjectName(_fromUtf8("actionExit"))
- self.actionHelp = QtGui.QAction(MainWindow)
- icon = QtGui.QIcon.fromTheme(_fromUtf8("help-contents"))
+ self.actionExit.setObjectName("actionExit")
+ self.actionHelp = QtWidgets.QAction(MainWindow)
+ icon = QtGui.QIcon.fromTheme("help-contents")
self.actionHelp.setIcon(icon)
- self.actionHelp.setObjectName(_fromUtf8("actionHelp"))
- self.actionSupport = QtGui.QAction(MainWindow)
- icon = QtGui.QIcon.fromTheme(_fromUtf8("help-support"))
+ self.actionHelp.setObjectName("actionHelp")
+ self.actionSupport = QtWidgets.QAction(MainWindow)
+ icon = QtGui.QIcon.fromTheme("help-support")
self.actionSupport.setIcon(icon)
- self.actionSupport.setObjectName(_fromUtf8("actionSupport"))
- self.actionAbout = QtGui.QAction(MainWindow)
- icon = QtGui.QIcon.fromTheme(_fromUtf8("help-about"))
+ self.actionSupport.setObjectName("actionSupport")
+ self.actionAbout = QtWidgets.QAction(MainWindow)
+ icon = QtGui.QIcon.fromTheme("help-about")
self.actionAbout.setIcon(icon)
- self.actionAbout.setObjectName(_fromUtf8("actionAbout"))
- self.actionSettings = QtGui.QAction(MainWindow)
- icon = QtGui.QIcon.fromTheme(_fromUtf8("document-properties"))
+ self.actionAbout.setObjectName("actionAbout")
+ self.actionSettings = QtWidgets.QAction(MainWindow)
+ icon = QtGui.QIcon.fromTheme("document-properties")
self.actionSettings.setIcon(icon)
- self.actionSettings.setObjectName(_fromUtf8("actionSettings"))
- self.actionRegenerateDeterministicAddresses = QtGui.QAction(MainWindow)
- icon = QtGui.QIcon.fromTheme(_fromUtf8("view-refresh"))
+ self.actionSettings.setObjectName("actionSettings")
+ self.actionRegenerateDeterministicAddresses = QtWidgets.QAction(MainWindow)
+ icon = QtGui.QIcon.fromTheme("view-refresh")
self.actionRegenerateDeterministicAddresses.setIcon(icon)
- self.actionRegenerateDeterministicAddresses.setObjectName(_fromUtf8("actionRegenerateDeterministicAddresses"))
- self.actionDeleteAllTrashedMessages = QtGui.QAction(MainWindow)
- icon = QtGui.QIcon.fromTheme(_fromUtf8("user-trash"))
+ self.actionRegenerateDeterministicAddresses.setObjectName("actionRegenerateDeterministicAddresses")
+ self.actionDeleteAllTrashedMessages = QtWidgets.QAction(MainWindow)
+ icon = QtGui.QIcon.fromTheme("user-trash")
self.actionDeleteAllTrashedMessages.setIcon(icon)
- self.actionDeleteAllTrashedMessages.setObjectName(_fromUtf8("actionDeleteAllTrashedMessages"))
- self.actionJoinChan = QtGui.QAction(MainWindow)
- icon = QtGui.QIcon.fromTheme(_fromUtf8("contact-new"))
+ self.actionDeleteAllTrashedMessages.setObjectName("actionDeleteAllTrashedMessages")
+ self.actionJoinChan = QtWidgets.QAction(MainWindow)
+ icon = QtGui.QIcon.fromTheme("contact-new")
self.actionJoinChan.setIcon(icon)
- self.actionJoinChan.setObjectName(_fromUtf8("actionJoinChan"))
+ self.actionJoinChan.setObjectName("actionJoinChan")
self.menuFile.addAction(self.actionManageKeys)
self.menuFile.addAction(self.actionDeleteAllTrashedMessages)
self.menuFile.addAction(self.actionRegenerateDeterministicAddresses)
@@ -672,11 +634,11 @@ class Ui_MainWindow(object):
# Popup menu actions container for the Sent page
# pylint: disable=attribute-defined-outside-init
- self.sentContextMenuToolbar = QtGui.QToolBar()
+ self.sentContextMenuToolbar = QtWidgets.QToolBar()
# Popup menu actions container for chans tree
- self.addressContextMenuToolbar = QtGui.QToolBar()
+ self.addressContextMenuToolbar = QtWidgets.QToolBar()
# Popup menu actions container for subscriptions tree
- self.subscriptionsContextMenuToolbar = QtGui.QToolBar()
+ self.subscriptionsContextMenuToolbar = QtWidgets.QToolBar()
def updateNetworkSwitchMenuLabel(self, dontconnect=None):
if dontconnect is None:
@@ -733,9 +695,7 @@ class Ui_MainWindow(object):
hours = int(config.getint('bitmessagesettings', 'ttl') / 60 / 60)
except:
pass
- self.labelHumanFriendlyTTLDescription.setText(
- _translate("MainWindow", "%n hour(s)", None, QtCore.QCoreApplication.CodecForTr, hours)
- )
+ self.labelHumanFriendlyTTLDescription.setText(_translate("MainWindow", "%n hour(s)", None, hours))
self.pushButtonClear.setText(_translate("MainWindow", "Clear", None))
self.pushButtonSend.setText(_translate("MainWindow", "Send", None))
self.tabWidget.setTabText(self.tabWidget.indexOf(self.send), _translate("MainWindow", "Send", None))
@@ -810,7 +770,7 @@ class Ui_MainWindow(object):
if __name__ == "__main__":
import sys
- app = QtGui.QApplication(sys.argv)
+ app = QtWidgets.QApplication(sys.argv)
MainWindow = settingsmixin.SMainWindow()
ui = Ui_MainWindow()
ui.setupUi(MainWindow)
diff --git a/src/bitmessageqt/blacklist.py b/src/bitmessageqt/blacklist.py
index 093f23d8..21c881e9 100644
--- a/src/bitmessageqt/blacklist.py
+++ b/src/bitmessageqt/blacklist.py
@@ -1,42 +1,44 @@
-from PyQt4 import QtCore, QtGui
+from unqstr import ustr, unic
+from dbcompat import dbstr
+from qtpy import QtCore, QtGui, QtWidgets
-import widgets
+from bitmessageqt import widgets
from addresses import addBMIfNotPresent
from bmconfigparser import config
-from dialogs import AddAddressDialog
+from .dialogs import AddAddressDialog
from helper_sql import sqlExecute, sqlQuery
from queues import UISignalQueue
-from retranslateui import RetranslateMixin
+from .retranslateui import RetranslateMixin
from tr import _translate
-from uisignaler import UISignaler
-from utils import avatarize
+from .uisignaler import UISignaler
+from .utils import avatarize
-class Blacklist(QtGui.QWidget, RetranslateMixin):
+class Blacklist(QtWidgets.QWidget, RetranslateMixin):
def __init__(self, parent=None):
super(Blacklist, self).__init__(parent)
widgets.load('blacklist.ui', self)
- QtCore.QObject.connect(self.radioButtonBlacklist, QtCore.SIGNAL(
- "clicked()"), self.click_radioButtonBlacklist)
- QtCore.QObject.connect(self.radioButtonWhitelist, QtCore.SIGNAL(
- "clicked()"), self.click_radioButtonWhitelist)
- QtCore.QObject.connect(self.pushButtonAddBlacklist, QtCore.SIGNAL(
- "clicked()"), self.click_pushButtonAddBlacklist)
+ self.radioButtonBlacklist.clicked.connect(
+ self.click_radioButtonBlacklist)
+ self.radioButtonWhitelist.clicked.connect(
+ self.click_radioButtonWhitelist)
+ self.pushButtonAddBlacklist.clicked.connect(
+ self.click_pushButtonAddBlacklist)
self.init_blacklist_popup_menu()
- # Initialize blacklist
- QtCore.QObject.connect(self.tableWidgetBlacklist, QtCore.SIGNAL(
- "itemChanged(QTableWidgetItem *)"), self.tableWidgetBlacklistItemChanged)
+ self.tableWidgetBlacklist.itemChanged.connect(
+ self.tableWidgetBlacklistItemChanged)
# Set the icon sizes for the identicons
- identicon_size = 3*7
- self.tableWidgetBlacklist.setIconSize(QtCore.QSize(identicon_size, identicon_size))
+ identicon_size = 3 * 7
+ self.tableWidgetBlacklist.setIconSize(
+ QtCore.QSize(identicon_size, identicon_size))
self.UISignalThread = UISignaler.get()
- QtCore.QObject.connect(self.UISignalThread, QtCore.SIGNAL(
- "rerenderBlackWhiteList()"), self.rerenderBlackWhiteList)
+ self.UISignalThread.rerenderBlackWhiteList.connect(
+ self.rerenderBlackWhiteList)
def click_radioButtonBlacklist(self):
if config.get('bitmessagesettings', 'blackwhitelist') == 'white':
@@ -59,30 +61,30 @@ class Blacklist(QtGui.QWidget, RetranslateMixin):
if self.NewBlacklistDialogInstance.exec_():
if self.NewBlacklistDialogInstance.labelAddressCheck.text() == \
_translate("MainWindow", "Address is valid."):
- address = addBMIfNotPresent(str(
+ address = addBMIfNotPresent(ustr(
self.NewBlacklistDialogInstance.lineEditAddress.text()))
# First we must check to see if the address is already in the
# address book. The user cannot add it again or else it will
# cause problems when updating and deleting the entry.
- t = (address,)
+ t = (dbstr(address),)
if config.get('bitmessagesettings', 'blackwhitelist') == 'black':
sql = '''select * from blacklist where address=?'''
else:
sql = '''select * from whitelist where address=?'''
- queryreturn = sqlQuery(sql,*t)
+ queryreturn = sqlQuery(sql, *t)
if queryreturn == []:
self.tableWidgetBlacklist.setSortingEnabled(False)
self.tableWidgetBlacklist.insertRow(0)
- newItem = QtGui.QTableWidgetItem(unicode(
- self.NewBlacklistDialogInstance.lineEditLabel.text().toUtf8(), 'utf-8'))
+ newItem = QtGui.QTableWidgetItem(unic(ustr(
+ self.NewBlacklistDialogInstance.lineEditLabel.text())))
newItem.setIcon(avatarize(address))
self.tableWidgetBlacklist.setItem(0, 0, newItem)
- newItem = QtGui.QTableWidgetItem(address)
+ newItem = QtWidgets.QTableWidgetItem(address)
newItem.setFlags(
QtCore.Qt.ItemIsSelectable | QtCore.Qt.ItemIsEnabled)
self.tableWidgetBlacklist.setItem(0, 1, newItem)
self.tableWidgetBlacklist.setSortingEnabled(True)
- t = (str(self.NewBlacklistDialogInstance.lineEditLabel.text().toUtf8()), address, True)
+ t = (dbstr(self.NewBlacklistDialogInstance.lineEditLabel.text()), dbstr(address), True)
if config.get('bitmessagesettings', 'blackwhitelist') == 'black':
sql = '''INSERT INTO blacklist VALUES (?,?,?)'''
else:
@@ -108,17 +110,17 @@ class Blacklist(QtGui.QWidget, RetranslateMixin):
def tableWidgetBlacklistItemChanged(self, item):
if item.column() == 0:
addressitem = self.tableWidgetBlacklist.item(item.row(), 1)
- if isinstance(addressitem, QtGui.QTableWidgetItem):
+ if isinstance(addressitem, QtWidgets.QTableWidgetItem):
if self.radioButtonBlacklist.isChecked():
sqlExecute('''UPDATE blacklist SET label=? WHERE address=?''',
- str(item.text()), str(addressitem.text()))
+ dbstr(item.text()), dbstr(addressitem.text()))
else:
sqlExecute('''UPDATE whitelist SET label=? WHERE address=?''',
- str(item.text()), str(addressitem.text()))
+ dbstr(item.text()), dbstr(addressitem.text()))
def init_blacklist_popup_menu(self, connectSignal=True):
# Popup menu for the Blacklist page
- self.blacklistContextMenuToolbar = QtGui.QToolBar()
+ self.blacklistContextMenuToolbar = QtWidgets.QToolBar()
# Actions
self.actionBlacklistNew = self.blacklistContextMenuToolbar.addAction(
_translate(
@@ -143,10 +145,9 @@ class Blacklist(QtGui.QWidget, RetranslateMixin):
self.tableWidgetBlacklist.setContextMenuPolicy(
QtCore.Qt.CustomContextMenu)
if connectSignal:
- self.connect(self.tableWidgetBlacklist, QtCore.SIGNAL(
- 'customContextMenuRequested(const QPoint&)'),
- self.on_context_menuBlacklist)
- self.popMenuBlacklist = QtGui.QMenu(self)
+ self.tableWidgetBlacklist.customContextMenuRequested.connect(
+ self.on_context_menuBlacklist)
+ self.popMenuBlacklist = QtWidgets.QMenu(self)
# self.popMenuBlacklist.addAction( self.actionBlacklistNew )
self.popMenuBlacklist.addAction(self.actionBlacklistDelete)
self.popMenuBlacklist.addSeparator()
@@ -171,17 +172,19 @@ class Blacklist(QtGui.QWidget, RetranslateMixin):
self.tableWidgetBlacklist.setSortingEnabled(False)
for row in queryreturn:
label, address, enabled = row
+ label = label.decode("utf-8", "replace")
+ address = address.decode("utf-8", "replace")
self.tableWidgetBlacklist.insertRow(0)
- newItem = QtGui.QTableWidgetItem(unicode(label, 'utf-8'))
+ newItem = QtWidgets.QTableWidgetItem(unic(ustr(label)))
if not enabled:
- newItem.setTextColor(QtGui.QColor(128, 128, 128))
+ newItem.setForeground(QtGui.QColor(128, 128, 128))
newItem.setIcon(avatarize(address))
self.tableWidgetBlacklist.setItem(0, 0, newItem)
- newItem = QtGui.QTableWidgetItem(address)
+ newItem = QtWidgets.QTableWidgetItem(address)
newItem.setFlags(
QtCore.Qt.ItemIsSelectable | QtCore.Qt.ItemIsEnabled)
if not enabled:
- newItem.setTextColor(QtGui.QColor(128, 128, 128))
+ newItem.setForeground(QtGui.QColor(128, 128, 128))
self.tableWidgetBlacklist.setItem(0, 1, newItem)
self.tableWidgetBlacklist.setSortingEnabled(True)
@@ -191,26 +194,26 @@ class Blacklist(QtGui.QWidget, RetranslateMixin):
def on_action_BlacklistDelete(self):
currentRow = self.tableWidgetBlacklist.currentRow()
- labelAtCurrentRow = self.tableWidgetBlacklist.item(
- currentRow, 0).text().toUtf8()
+ labelAtCurrentRow = ustr(self.tableWidgetBlacklist.item(
+ currentRow, 0).text())
addressAtCurrentRow = self.tableWidgetBlacklist.item(
currentRow, 1).text()
if config.get('bitmessagesettings', 'blackwhitelist') == 'black':
sqlExecute(
'''DELETE FROM blacklist WHERE label=? AND address=?''',
- str(labelAtCurrentRow), str(addressAtCurrentRow))
+ dbstr(labelAtCurrentRow), dbstr(addressAtCurrentRow))
else:
sqlExecute(
'''DELETE FROM whitelist WHERE label=? AND address=?''',
- str(labelAtCurrentRow), str(addressAtCurrentRow))
+ dbstr(labelAtCurrentRow), dbstr(addressAtCurrentRow))
self.tableWidgetBlacklist.removeRow(currentRow)
def on_action_BlacklistClipboard(self):
currentRow = self.tableWidgetBlacklist.currentRow()
addressAtCurrentRow = self.tableWidgetBlacklist.item(
currentRow, 1).text()
- clipboard = QtGui.QApplication.clipboard()
- clipboard.setText(str(addressAtCurrentRow))
+ clipboard = QtWidgets.QApplication.clipboard()
+ clipboard.setText(ustr(addressAtCurrentRow))
def on_context_menuBlacklist(self, point):
self.popMenuBlacklist.exec_(
@@ -220,33 +223,33 @@ class Blacklist(QtGui.QWidget, RetranslateMixin):
currentRow = self.tableWidgetBlacklist.currentRow()
addressAtCurrentRow = self.tableWidgetBlacklist.item(
currentRow, 1).text()
- self.tableWidgetBlacklist.item(
- currentRow, 0).setTextColor(QtGui.QApplication.palette().text().color())
- self.tableWidgetBlacklist.item(
- currentRow, 1).setTextColor(QtGui.QApplication.palette().text().color())
+ self.tableWidgetBlacklist.item(currentRow, 0).setForeground(
+ QtWidgets.QApplication.palette().text().color())
+ self.tableWidgetBlacklist.item(currentRow, 1).setForeground(
+ QtWidgets.QApplication.palette().text().color())
if config.get('bitmessagesettings', 'blackwhitelist') == 'black':
sqlExecute(
'''UPDATE blacklist SET enabled=1 WHERE address=?''',
- str(addressAtCurrentRow))
+ dbstr(addressAtCurrentRow))
else:
sqlExecute(
'''UPDATE whitelist SET enabled=1 WHERE address=?''',
- str(addressAtCurrentRow))
+ dbstr(addressAtCurrentRow))
def on_action_BlacklistDisable(self):
currentRow = self.tableWidgetBlacklist.currentRow()
addressAtCurrentRow = self.tableWidgetBlacklist.item(
currentRow, 1).text()
- self.tableWidgetBlacklist.item(
- currentRow, 0).setTextColor(QtGui.QColor(128, 128, 128))
- self.tableWidgetBlacklist.item(
- currentRow, 1).setTextColor(QtGui.QColor(128, 128, 128))
+ self.tableWidgetBlacklist.item(currentRow, 0).setForeground(
+ QtGui.QColor(128, 128, 128))
+ self.tableWidgetBlacklist.item(currentRow, 1).setForeground(
+ QtGui.QColor(128, 128, 128))
if config.get('bitmessagesettings', 'blackwhitelist') == 'black':
sqlExecute(
- '''UPDATE blacklist SET enabled=0 WHERE address=?''', str(addressAtCurrentRow))
+ '''UPDATE blacklist SET enabled=0 WHERE address=?''', dbstr(addressAtCurrentRow))
else:
sqlExecute(
- '''UPDATE whitelist SET enabled=0 WHERE address=?''', str(addressAtCurrentRow))
+ '''UPDATE whitelist SET enabled=0 WHERE address=?''', dbstr(addressAtCurrentRow))
def on_action_BlacklistSetAvatar(self):
self.window().on_action_SetAvatar(self.tableWidgetBlacklist)
diff --git a/src/bitmessageqt/blacklist.ui b/src/bitmessageqt/blacklist.ui
index 80993fac..c3b9b5a3 100644
--- a/src/bitmessageqt/blacklist.ui
+++ b/src/bitmessageqt/blacklist.ui
@@ -62,6 +62,9 @@
true
+
+ false
+ true
@@ -98,11 +101,8 @@
STableWidgetQTableWidget
- bitmessageqt/settingsmixin.h
+ bitmessageqt.settingsmixin
-
-
-
diff --git a/src/bitmessageqt/dialogs.py b/src/bitmessageqt/dialogs.py
index dc31e266..59f33ad5 100644
--- a/src/bitmessageqt/dialogs.py
+++ b/src/bitmessageqt/dialogs.py
@@ -1,22 +1,23 @@
"""
-Custom dialog classes
+All dialogs are available in this module.
"""
# pylint: disable=too-few-public-methods
-from PyQt4 import QtGui
+from unqstr import ustr
+
+from qtpy import QtWidgets
import paths
-import widgets
-from address_dialogs import (
+from bitmessageqt import widgets
+from .address_dialogs import (
AddAddressDialog, EmailGatewayDialog, NewAddressDialog,
NewSubscriptionDialog, RegenerateAddressesDialog,
SpecialAddressBehaviorDialog
)
-from newchandialog import NewChanDialog
-from settings import SettingsDialog
+from .newchandialog import NewChanDialog
+from .settings import SettingsDialog
from tr import _translate
from version import softwareVersion
-
__all__ = [
"NewChanDialog", "AddAddressDialog", "NewAddressDialog",
"NewSubscriptionDialog", "RegenerateAddressesDialog",
@@ -25,8 +26,8 @@ __all__ = [
]
-class AboutDialog(QtGui.QDialog):
- """The `About` dialog"""
+class AboutDialog(QtWidgets.QDialog):
+ """The "About" dialog"""
def __init__(self, parent=None):
super(AboutDialog, self).__init__(parent)
widgets.load('about.ui', self)
@@ -36,7 +37,7 @@ class AboutDialog(QtGui.QDialog):
if commit:
version += '-' + commit[:7]
self.labelVersion.setText(
- self.labelVersion.text().replace(
+ ustr(self.labelVersion.text()).replace(
':version:', version
).replace(':branch:', commit or 'v%s' % version)
)
@@ -44,17 +45,17 @@ class AboutDialog(QtGui.QDialog):
try:
self.label_2.setText(
- self.label_2.text().replace(
- '2022', str(last_commit.get('time').year)
+ ustr(self.label_2.text()).replace(
+ '2022', ustr(last_commit.get('time').year)
))
except AttributeError:
pass
- self.setFixedSize(QtGui.QWidget.sizeHint(self))
+ self.setFixedSize(QtWidgets.QWidget.sizeHint(self))
-class IconGlossaryDialog(QtGui.QDialog):
- """The `Icon Glossary` dialog, explaining the status icon colors"""
+class IconGlossaryDialog(QtWidgets.QDialog):
+ """The "Icon Glossary" dialog, explaining the status icon colors"""
def __init__(self, parent=None, config=None):
super(IconGlossaryDialog, self).__init__(parent)
widgets.load('iconglossary.ui', self)
@@ -64,22 +65,23 @@ class IconGlossaryDialog(QtGui.QDialog):
self.labelPortNumber.setText(_translate(
"iconGlossaryDialog",
- "You are using TCP port %1. (This can be changed in the settings)."
- ).arg(config.getint('bitmessagesettings', 'port')))
- self.setFixedSize(QtGui.QWidget.sizeHint(self))
+ "You are using TCP port {0}."
+ " (This can be changed in the settings)."
+ ).format(config.getint('bitmessagesettings', 'port')))
+ self.setFixedSize(QtWidgets.QWidget.sizeHint(self))
-class HelpDialog(QtGui.QDialog):
- """The `Help` dialog"""
+class HelpDialog(QtWidgets.QDialog):
+ """The "Help" dialog"""
def __init__(self, parent=None):
super(HelpDialog, self).__init__(parent)
widgets.load('help.ui', self)
- self.setFixedSize(QtGui.QWidget.sizeHint(self))
+ self.setFixedSize(QtWidgets.QWidget.sizeHint(self))
-class ConnectDialog(QtGui.QDialog):
- """The `Connect` dialog"""
+class ConnectDialog(QtWidgets.QDialog):
+ """The "Connect" dialog"""
def __init__(self, parent=None):
super(ConnectDialog, self).__init__(parent)
widgets.load('connect.ui', self)
- self.setFixedSize(QtGui.QWidget.sizeHint(self))
+ self.setFixedSize(QtWidgets.QWidget.sizeHint(self))
diff --git a/src/bitmessageqt/foldertree.py b/src/bitmessageqt/foldertree.py
index c50b7d3d..db09a5d6 100644
--- a/src/bitmessageqt/foldertree.py
+++ b/src/bitmessageqt/foldertree.py
@@ -1,18 +1,23 @@
"""
Folder tree and messagelist widgets definitions.
"""
-# pylint: disable=too-many-arguments,bad-super-call
+# pylint: disable=too-many-arguments
# pylint: disable=attribute-defined-outside-init
-from cgi import escape
+try:
+ from cgi import escape
+except ImportError:
+ from html import escape
-from PyQt4 import QtCore, QtGui
+from unqstr import ustr, unic
+from dbcompat import dbstr
+from qtpy import QtCore, QtGui, QtWidgets
from bmconfigparser import config
from helper_sql import sqlExecute, sqlQuery
-from settingsmixin import SettingsMixin
+from .settingsmixin import SettingsMixin
from tr import _translate
-from utils import avatarize
+from .utils import avatarize
# for pylupdate
_translate("MainWindow", "inbox")
@@ -38,15 +43,16 @@ class AccountMixin(object):
return QtGui.QColor(128, 128, 128)
elif self.type == self.CHAN:
return QtGui.QColor(216, 119, 0)
- elif self.type in [self.MAILINGLIST, self.SUBSCRIPTION]:
+ elif self.type in (self.MAILINGLIST, self.SUBSCRIPTION):
return QtGui.QColor(137, 4, 177)
- return QtGui.QApplication.palette().text().color()
+
+ return QtWidgets.QApplication.palette().text().color()
def folderColor(self):
"""QT UI color for a folder"""
if not self.parent().isEnabled:
return QtGui.QColor(128, 128, 128)
- return QtGui.QApplication.palette().text().color()
+ return QtWidgets.QApplication.palette().text().color()
def accountBrush(self):
"""Account brush (for QT UI)"""
@@ -62,7 +68,7 @@ class AccountMixin(object):
def accountString(self):
"""Account string suitable for use in To: field: label """
- label = self._getLabel()
+ label = ustr(self._getLabel())
return (
self.address if label == self.address
else '%s <%s>' % (label, self.address)
@@ -73,7 +79,7 @@ class AccountMixin(object):
if address is None:
self.address = None
else:
- self.address = str(address)
+ self.address = ustr(address)
def setUnreadCount(self, cnt):
"""Set number of unread messages"""
@@ -83,7 +89,7 @@ class AccountMixin(object):
except AttributeError:
pass
self.unreadCount = int(cnt)
- if isinstance(self, QtGui.QTreeWidgetItem):
+ if isinstance(self, QtWidgets.QTreeWidgetItem):
self.emitDataChanged()
def setEnabled(self, enabled):
@@ -97,7 +103,7 @@ class AccountMixin(object):
for i in range(self.childCount()):
if isinstance(self.child(i), Ui_FolderWidget):
self.child(i).setEnabled(enabled)
- if isinstance(self, QtGui.QTreeWidgetItem):
+ if isinstance(self, QtWidgets.QTreeWidgetItem):
self.emitDataChanged()
def setType(self):
@@ -111,53 +117,53 @@ class AccountMixin(object):
elif config.safeGetBoolean(self.address, 'mailinglist'):
self.type = self.MAILINGLIST
elif sqlQuery(
- '''select label from subscriptions where address=?''', self.address):
+ 'SELECT label FROM subscriptions WHERE address=?',
+ dbstr(self.address)
+ ):
self.type = AccountMixin.SUBSCRIPTION
else:
self.type = self.NORMAL
def defaultLabel(self):
"""Default label (in case no label is set manually)"""
- queryreturn = None
- retval = None
+ queryreturn = retval = None
if self.type in (
AccountMixin.NORMAL,
AccountMixin.CHAN, AccountMixin.MAILINGLIST):
try:
- retval = unicode(
- config.get(self.address, 'label'), 'utf-8')
+ retval = unic(ustr(config.get(self.address, 'label')))
except Exception:
queryreturn = sqlQuery(
- '''select label from addressbook where address=?''', self.address)
+ 'SELECT label FROM addressbook WHERE address=?',
+ dbstr(self.address)
+ )
elif self.type == AccountMixin.SUBSCRIPTION:
queryreturn = sqlQuery(
- '''select label from subscriptions where address=?''', self.address)
- if queryreturn is not None:
- if queryreturn != []:
- for row in queryreturn:
- retval, = row
- retval = unicode(retval, 'utf-8')
+ 'SELECT label FROM subscriptions WHERE address=?',
+ dbstr(self.address)
+ )
+ if queryreturn:
+ retval = unic(ustr(queryreturn[-1][0]))
elif self.address is None or self.type == AccountMixin.ALL:
- return unicode(
- str(_translate("MainWindow", "All accounts")), 'utf-8')
+ return unic(_translate("MainWindow", "All accounts"))
- return retval or unicode(self.address, 'utf-8')
+ return retval or unic(self.address)
-class BMTreeWidgetItem(QtGui.QTreeWidgetItem, AccountMixin):
+class BMTreeWidgetItem(QtWidgets.QTreeWidgetItem, AccountMixin):
"""A common abstract class for Tree widget item"""
def __init__(self, parent, pos, address, unreadCount):
- super(QtGui.QTreeWidgetItem, self).__init__()
+ super(QtWidgets.QTreeWidgetItem, self).__init__()
self.setAddress(address)
self.setUnreadCount(unreadCount)
self._setup(parent, pos)
def _getAddressBracket(self, unreadCount=False):
- return " (" + str(self.unreadCount) + ")" if unreadCount else ""
+ return " (" + ustr(self.unreadCount) + ")" if unreadCount else ""
def data(self, column, role):
- """Override internal QT method for returning object data"""
+ """Override internal Qt method for returning object data"""
if column == 0:
if role == QtCore.Qt.DisplayRole:
return self._getLabel() + self._getAddressBracket(
@@ -190,11 +196,11 @@ class Ui_FolderWidget(BMTreeWidgetItem):
return _translate("MainWindow", self.folderName)
def setFolderName(self, fname):
- """Set folder name (for QT UI)"""
- self.folderName = str(fname)
+ """Set folder name (for Qt UI)"""
+ self.folderName = ustr(fname)
def data(self, column, role):
- """Override internal QT method for returning object data"""
+ """Override internal Qt method for returning object data"""
if column == 0 and role == QtCore.Qt.ForegroundRole:
return self.folderBrush()
return super(Ui_FolderWidget, self).data(column, role)
@@ -216,12 +222,14 @@ class Ui_FolderWidget(BMTreeWidgetItem):
return self.folderName < other.folderName
return x >= y if reverse else x < y
- return super(QtGui.QTreeWidgetItem, self).__lt__(other)
+ return super(QtWidgets.QTreeWidgetItem, self).__lt__(other)
class Ui_AddressWidget(BMTreeWidgetItem, SettingsMixin):
"""Item in the account/folder tree representing an account"""
- def __init__(self, parent, pos=0, address=None, unreadCount=0, enabled=True):
+ def __init__(
+ self, parent, pos=0, address=None, unreadCount=0, enabled=True
+ ):
super(Ui_AddressWidget, self).__init__(
parent, pos, address, unreadCount)
self.setEnabled(enabled)
@@ -232,15 +240,13 @@ class Ui_AddressWidget(BMTreeWidgetItem, SettingsMixin):
def _getLabel(self):
if self.address is None:
- return unicode(_translate(
- "MainWindow", "All accounts").toUtf8(), 'utf-8', 'ignore')
+ return unic(_translate("MainWindow", "All accounts"))
else:
try:
- return unicode(
- config.get(self.address, 'label'),
- 'utf-8', 'ignore')
+ return unic(ustr(
+ config.get(self.address, 'label')))
except:
- return unicode(self.address, 'utf-8')
+ return unic(self.address)
def _getAddressBracket(self, unreadCount=False):
ret = "" if self.isExpanded() \
@@ -260,15 +266,13 @@ class Ui_AddressWidget(BMTreeWidgetItem, SettingsMixin):
return super(Ui_AddressWidget, self).data(column, role)
def setData(self, column, role, value):
- """Save account label (if you edit in the the UI, this will be triggered and will save it to keys.dat)"""
+ """
+ Save account label (if you edit in the the UI, this will be
+ triggered and will save it to keys.dat)
+ """
if role == QtCore.Qt.EditRole \
and self.type != AccountMixin.SUBSCRIPTION:
- config.set(
- str(self.address), 'label',
- str(value.toString().toUtf8())
- if isinstance(value, QtCore.QVariant)
- else value.encode('utf-8')
- )
+ config.set(self.address, 'label', ustr(value))
config.save()
return super(Ui_AddressWidget, self).setData(column, role, value)
@@ -295,24 +299,26 @@ class Ui_AddressWidget(BMTreeWidgetItem, SettingsMixin):
if self._getSortRank() < other._getSortRank() else reverse
)
- return super(QtGui.QTreeWidgetItem, self).__lt__(other)
+ return super(Ui_AddressWidget, self).__lt__(other)
class Ui_SubscriptionWidget(Ui_AddressWidget):
"""Special treating of subscription addresses"""
# pylint: disable=unused-argument
- def __init__(self, parent, pos=0, address="", unreadCount=0, label="", enabled=True):
+ def __init__(
+ self, parent, pos=0, address="", unreadCount=0, label="",
+ enabled=True
+ ):
super(Ui_SubscriptionWidget, self).__init__(
parent, pos, address, unreadCount, enabled)
def _getLabel(self):
queryreturn = sqlQuery(
- '''select label from subscriptions where address=?''', self.address)
- if queryreturn != []:
- for row in queryreturn:
- retval, = row
- return unicode(retval, 'utf-8', 'ignore')
- return unicode(self.address, 'utf-8')
+ 'SELECT label FROM subscriptions WHERE address=?',
+ dbstr(self.address))
+ if queryreturn:
+ return unic(ustr(queryreturn[-1][0]))
+ return unic(self.address)
def setType(self):
"""Set account type"""
@@ -322,22 +328,17 @@ class Ui_SubscriptionWidget(Ui_AddressWidget):
def setData(self, column, role, value):
"""Save subscription label to database"""
if role == QtCore.Qt.EditRole:
- if isinstance(value, QtCore.QVariant):
- label = str(
- value.toString().toUtf8()).decode('utf-8', 'ignore')
- else:
- label = unicode(value, 'utf-8', 'ignore')
sqlExecute(
- '''UPDATE subscriptions SET label=? WHERE address=?''',
- label, self.address)
+ 'UPDATE subscriptions SET label=? WHERE address=?',
+ dbstr(unic(ustr(value))), dbstr(self.address))
return super(Ui_SubscriptionWidget, self).setData(column, role, value)
-class BMTableWidgetItem(QtGui.QTableWidgetItem, SettingsMixin):
+class BMTableWidgetItem(QtWidgets.QTableWidgetItem, SettingsMixin):
"""A common abstract class for Table widget item"""
def __init__(self, label=None, unread=False):
- super(QtGui.QTableWidgetItem, self).__init__()
+ super(QtWidgets.QTableWidgetItem, self).__init__()
self.setLabel(label)
self.setUnread(unread)
self._setup()
@@ -407,18 +408,17 @@ class MessageList_AddressWidget(BMAddressWidget):
AccountMixin.NORMAL,
AccountMixin.CHAN, AccountMixin.MAILINGLIST):
try:
- newLabel = unicode(
- config.get(self.address, 'label'),
- 'utf-8', 'ignore')
+ newLabel = unic(ustr(config.get(self.address, 'label')))
except:
queryreturn = sqlQuery(
- '''select label from addressbook where address=?''', self.address)
+ 'SELECT label FROM addressbook WHERE address=?',
+ dbstr(self.address))
elif self.type == AccountMixin.SUBSCRIPTION:
queryreturn = sqlQuery(
- '''select label from subscriptions where address=?''', self.address)
+ 'SELECT label FROM subscriptions WHERE address=?',
+ dbstr(self.address))
if queryreturn:
- for row in queryreturn:
- newLabel = unicode(row[0], 'utf-8', 'ignore')
+ newLabel = unic(ustr(queryreturn[-1][0]))
self.label = newLabel
@@ -438,7 +438,7 @@ class MessageList_AddressWidget(BMAddressWidget):
def __lt__(self, other):
if isinstance(other, MessageList_AddressWidget):
return self.label.lower() < other.label.lower()
- return super(QtGui.QTableWidgetItem, self).__lt__(other)
+ return super(MessageList_AddressWidget, self).__lt__(other)
class MessageList_SubjectWidget(BMTableWidgetItem):
@@ -454,16 +454,16 @@ class MessageList_SubjectWidget(BMTableWidgetItem):
def data(self, role):
"""Return object data (QT UI)"""
if role == QtCore.Qt.UserRole:
- return self.subject
+ return ustr(self.subject)
if role == QtCore.Qt.ToolTipRole:
- return escape(unicode(self.subject, 'utf-8'))
+ return escape(unic(ustr(self.subject)))
return super(MessageList_SubjectWidget, self).data(role)
# label (or address) alphabetically, disabled at the end
def __lt__(self, other):
if isinstance(other, MessageList_SubjectWidget):
return self.label.lower() < other.label.lower()
- return super(QtGui.QTableWidgetItem, self).__lt__(other)
+ return super(MessageList_SubjectWidget, self).__lt__(other)
# In order for the time columns on the Inbox and Sent tabs to be sorted
@@ -476,9 +476,9 @@ class MessageList_TimeWidget(BMTableWidgetItem):
msgid is available by QtCore.Qt.UserRole
"""
- def __init__(self, label=None, unread=False, timestamp=None, msgid=''):
+ def __init__(self, label=None, unread=False, timestamp=None, msgid=b''):
super(MessageList_TimeWidget, self).__init__(label, unread)
- self.setData(QtCore.Qt.UserRole, QtCore.QByteArray(msgid))
+ self.setData(QtCore.Qt.UserRole, QtCore.QByteArray(bytes(msgid)))
self.setData(TimestampRole, int(timestamp))
def __lt__(self, other):
@@ -491,15 +491,14 @@ class MessageList_TimeWidget(BMTableWidgetItem):
"""
data = super(MessageList_TimeWidget, self).data(role)
if role == TimestampRole:
- return int(data.toPyObject())
+ return int(data)
if role == QtCore.Qt.UserRole:
- return str(data.toPyObject())
+ return ustr(data)
return data
class Ui_AddressBookWidgetItem(BMAddressWidget):
"""Addressbook item"""
- # pylint: disable=unused-argument
def __init__(self, label=None, acc_type=AccountMixin.NORMAL):
self.type = acc_type
super(Ui_AddressBookWidgetItem, self).__init__(label=label)
@@ -513,10 +512,7 @@ class Ui_AddressBookWidgetItem(BMAddressWidget):
def setData(self, role, value):
"""Set data"""
if role == QtCore.Qt.EditRole:
- self.label = str(
- value.toString().toUtf8()
- if isinstance(value, QtCore.QVariant) else value
- )
+ self.label = ustr(value)
if self.type in (
AccountMixin.NORMAL,
AccountMixin.MAILINGLIST, AccountMixin.CHAN):
@@ -525,28 +521,33 @@ class Ui_AddressBookWidgetItem(BMAddressWidget):
config.set(self.address, 'label', self.label)
config.save()
except:
- sqlExecute('''UPDATE addressbook set label=? WHERE address=?''', self.label, self.address)
+ sqlExecute(
+ 'UPDATE addressbook SET label=? WHERE address=?',
+ dbstr(self.label), dbstr(self.address)
+ )
elif self.type == AccountMixin.SUBSCRIPTION:
- sqlExecute('''UPDATE subscriptions set label=? WHERE address=?''', self.label, self.address)
- else:
- pass
+ sqlExecute(
+ 'UPDATE subscriptions SET label=? WHERE address=?',
+ dbstr(self.label), dbstr(self.address))
return super(Ui_AddressBookWidgetItem, self).setData(role, value)
def __lt__(self, other):
- if isinstance(other, Ui_AddressBookWidgetItem):
- reverse = QtCore.Qt.DescendingOrder == \
- self.tableWidget().horizontalHeader().sortIndicatorOrder()
+ if not isinstance(other, Ui_AddressBookWidgetItem):
+ return super(Ui_AddressBookWidgetItem, self).__lt__(other)
- if self.type == other.type:
- return self.label.lower() < other.label.lower()
- return not reverse if self.type < other.type else reverse
- return super(QtGui.QTableWidgetItem, self).__lt__(other)
+ reverse = QtCore.Qt.DescendingOrder == \
+ self.tableWidget().horizontalHeader().sortIndicatorOrder()
+
+ if self.type == other.type:
+ return self.label.lower() < other.label.lower()
+
+ return not reverse if self.type < other.type else reverse
class Ui_AddressBookWidgetItemLabel(Ui_AddressBookWidgetItem):
"""Addressbook label item"""
def __init__(self, address, label, acc_type):
- self.address = address
+ self.address = ustr(address)
super(Ui_AddressBookWidgetItemLabel, self).__init__(label, acc_type)
def data(self, role):
@@ -558,7 +559,7 @@ class Ui_AddressBookWidgetItemLabel(Ui_AddressBookWidgetItem):
class Ui_AddressBookWidgetItemAddress(Ui_AddressBookWidgetItem):
"""Addressbook address item"""
def __init__(self, address, label, acc_type):
- self.address = address
+ self.address = ustr(address)
super(Ui_AddressBookWidgetItemAddress, self).__init__(address, acc_type)
def data(self, role):
@@ -570,28 +571,27 @@ class Ui_AddressBookWidgetItemAddress(Ui_AddressBookWidgetItem):
return super(Ui_AddressBookWidgetItemAddress, self).data(role)
-class AddressBookCompleter(QtGui.QCompleter):
+class AddressBookCompleter(QtWidgets.QCompleter):
"""Addressbook completer"""
-
def __init__(self):
super(AddressBookCompleter, self).__init__()
self.cursorPos = -1
- def onCursorPositionChanged(self, oldPos, newPos): # pylint: disable=unused-argument
+ def onCursorPositionChanged(self, oldPos, newPos):
"""Callback for cursor position change"""
+ # pylint: disable=unused-argument
if oldPos != self.cursorPos:
self.cursorPos = -1
def splitPath(self, path):
"""Split on semicolon"""
- text = unicode(path.toUtf8(), 'utf-8')
+ text = unic(ustr(path))
return [text[:self.widget().cursorPosition()].split(';')[-1].strip()]
def pathFromIndex(self, index):
"""Perform autocompletion (reimplemented QCompleter method)"""
- autoString = unicode(
- index.data(QtCore.Qt.EditRole).toString().toUtf8(), 'utf-8')
- text = unicode(self.widget().text().toUtf8(), 'utf-8')
+ autoString = unic(ustr(index.data(QtCore.Qt.EditRole).toString()))
+ text = unic(ustr(self.widget().text()))
# If cursor position was saved, restore it, else save it
if self.cursorPos != -1:
@@ -620,7 +620,6 @@ class AddressBookCompleter(QtGui.QCompleter):
# Get string value from before auto finished string is selected
# pre = text[prevDelimiterIndex + 1:curIndex - 1]
-
# Get part of string that occurs AFTER cursor
part2 = text[nextDelimiterIndex:]
diff --git a/src/bitmessageqt/languagebox.py b/src/bitmessageqt/languagebox.py
index 34f96b02..c4b61154 100644
--- a/src/bitmessageqt/languagebox.py
+++ b/src/bitmessageqt/languagebox.py
@@ -1,48 +1,56 @@
-"""Language Box Module for Locale Settings"""
-# pylint: disable=too-few-public-methods,bad-continuation
+"""LanguageBox widget is for selecting UI language"""
+
import glob
import os
-from PyQt4 import QtCore, QtGui
+from qtpy import QtCore, QtWidgets
import paths
from bmconfigparser import config
+from tr import _translate
-class LanguageBox(QtGui.QComboBox):
- """LanguageBox class for Qt UI"""
+# pylint: disable=too-few-public-methods
+class LanguageBox(QtWidgets.QComboBox):
+ """A subclass of `QtWidgets.QComboBox` for selecting language"""
languageName = {
- "system": "System Settings", "eo": "Esperanto",
+ "system": "System Settings",
+ "eo": "Esperanto",
"en_pirate": "Pirate English"
}
def __init__(self, parent=None):
- super(QtGui.QComboBox, self).__init__(parent)
+ super(LanguageBox, self).__init__(parent)
self.populate()
def populate(self):
"""Populates drop down list with all available languages."""
self.clear()
localesPath = os.path.join(paths.codePath(), 'translations')
- self.addItem(QtGui.QApplication.translate(
- "settingsDialog", "System Settings", "system"), "system")
+ self.addItem(
+ _translate("settingsDialog", "System Settings", "system"),
+ "system"
+ )
self.setCurrentIndex(0)
- self.setInsertPolicy(QtGui.QComboBox.InsertAlphabetically)
+ self.setInsertPolicy(QtWidgets.QComboBox.InsertAlphabetically)
for translationFile in sorted(
glob.glob(os.path.join(localesPath, "bitmessage_*.qm"))
):
localeShort = \
os.path.split(translationFile)[1].split("_", 1)[1][:-3]
+ locale = QtCore.QLocale(localeShort)
if localeShort in LanguageBox.languageName:
self.addItem(
LanguageBox.languageName[localeShort], localeShort)
+ elif locale.nativeLanguageName() == "":
+ self.addItem(localeShort, localeShort)
else:
locale = QtCore.QLocale(localeShort)
self.addItem(
locale.nativeLanguageName() or localeShort, localeShort)
configuredLocale = config.safeGet(
- 'bitmessagesettings', 'userlocale', "system")
+ 'bitmessagesettings', 'userlocale', 'system')
for i in range(self.count()):
if self.itemData(i) == configuredLocale:
self.setCurrentIndex(i)
diff --git a/src/bitmessageqt/messagecompose.py b/src/bitmessageqt/messagecompose.py
index c51282f8..68de5ce0 100644
--- a/src/bitmessageqt/messagecompose.py
+++ b/src/bitmessageqt/messagecompose.py
@@ -1,33 +1,34 @@
-"""
-Message editor with a wheel zoom functionality
-"""
-# pylint: disable=bad-continuation
+"""The MessageCompose class definition"""
-from PyQt4 import QtCore, QtGui
+from qtpy import QtCore, QtWidgets
+from tr import _translate
-class MessageCompose(QtGui.QTextEdit):
+class MessageCompose(QtWidgets.QTextEdit):
"""Editor class with wheel zoom functionality"""
- def __init__(self, parent=0):
+ def __init__(self, parent=None):
super(MessageCompose, self).__init__(parent)
+ # we'll deal with this later when we have a new message format
self.setAcceptRichText(False)
self.defaultFontPointSize = self.currentFont().pointSize()
def wheelEvent(self, event):
"""Mouse wheel scroll event handler"""
if (
- QtGui.QApplication.queryKeyboardModifiers() & QtCore.Qt.ControlModifier
- ) == QtCore.Qt.ControlModifier and event.orientation() == QtCore.Qt.Vertical:
+ (QtWidgets.QApplication.queryKeyboardModifiers()
+ & QtCore.Qt.ControlModifier) == QtCore.Qt.ControlModifier
+ and event.angleDelta().y() != 0
+ ):
if event.delta() > 0:
self.zoomIn(1)
else:
self.zoomOut(1)
- zoom = self.currentFont().pointSize() * 100 / self.defaultFontPointSize
- QtGui.QApplication.activeWindow().statusBar().showMessage(
- QtGui.QApplication.translate("MainWindow", "Zoom level %1%").arg(
- str(zoom)
- )
- )
+ QtWidgets.QApplication.activeWindow().statusbar.showMessage(
+ _translate("MainWindow", "Zoom level {0}%").format(
+ # zoom percentage
+ self.currentFont().pointSize() * 100
+ / self.defaultFontPointSize
+ ))
else:
# in QTextEdit, super does not zoom, only scroll
super(MessageCompose, self).wheelEvent(event)
diff --git a/src/bitmessageqt/messageview.py b/src/bitmessageqt/messageview.py
index 13ea16f9..3323fa67 100644
--- a/src/bitmessageqt/messageview.py
+++ b/src/bitmessageqt/messageview.py
@@ -1,22 +1,22 @@
"""
Custom message viewer with support for switching between HTML and plain
text rendering, HTML sanitization, lazy rendering (as you scroll down),
-zoom and URL click warning popup
-
+zoom and URL click warning popup.
"""
-from PyQt4 import QtCore, QtGui
+from unqstr import ustr, unic
+from qtpy import QtCore, QtGui, QtWidgets
-from safehtmlparser import SafeHTMLParser
+from .safehtmlparser import SafeHTMLParser
from tr import _translate
-class MessageView(QtGui.QTextBrowser):
+class MessageView(QtWidgets.QTextBrowser):
"""Message content viewer class, can switch between plaintext and HTML"""
MODE_PLAIN = 0
MODE_HTML = 1
- def __init__(self, parent=0):
+ def __init__(self, parent=None):
super(MessageView, self).__init__(parent)
self.mode = MessageView.MODE_PLAIN
self.html = None
@@ -38,8 +38,11 @@ class MessageView(QtGui.QTextBrowser):
def mousePressEvent(self, event):
"""Mouse press button event handler"""
- if event.button() == QtCore.Qt.LeftButton and self.html and self.html.has_html and self.cursorForPosition(
- event.pos()).block().blockNumber() == 0:
+ if (
+ event.button() == QtCore.Qt.LeftButton
+ and self.html and self.html.has_html
+ and self.cursorForPosition(event.pos()).block().blockNumber() == 0
+ ):
if self.mode == MessageView.MODE_PLAIN:
self.showHTML()
else:
@@ -52,23 +55,23 @@ class MessageView(QtGui.QTextBrowser):
# super will actually automatically take care of zooming
super(MessageView, self).wheelEvent(event)
if (
- QtGui.QApplication.queryKeyboardModifiers() & QtCore.Qt.ControlModifier
- ) == QtCore.Qt.ControlModifier and event.orientation() == QtCore.Qt.Vertical:
+ (QtWidgets.QApplication.queryKeyboardModifiers()
+ & QtCore.Qt.ControlModifier) == QtCore.Qt.ControlModifier
+ and event.angleDelta().y() != 0
+ ):
zoom = self.currentFont().pointSize() * 100 / self.defaultFontPointSize
- QtGui.QApplication.activeWindow().statusBar().showMessage(_translate(
- "MainWindow", "Zoom level %1%").arg(str(zoom)))
+ QtWidgets.QApplication.activeWindow().statusbar.showMessage(
+ _translate("MainWindow", "Zoom level {0}%").format(zoom))
def setWrappingWidth(self, width=None):
"""Set word-wrapping width"""
- self.setLineWrapMode(QtGui.QTextEdit.FixedPixelWidth)
- if width is None:
- width = self.width()
- self.setLineWrapColumnOrWidth(width)
+ self.setLineWrapMode(QtWidgets.QTextEdit.FixedPixelWidth)
+ self.setLineWrapColumnOrWidth(width or self.width())
def confirmURL(self, link):
"""Show a dialog requesting URL opening confirmation"""
if link.scheme() == "mailto":
- window = QtGui.QApplication.activeWindow()
+ window = QtWidgets.QApplication.activeWindow()
window.ui.lineEditTo.setText(link.path())
if link.hasQueryItem("subject"):
window.ui.lineEditSubject.setText(
@@ -83,39 +86,40 @@ class MessageView(QtGui.QTextBrowser):
)
window.ui.textEditMessage.setFocus()
return
- reply = QtGui.QMessageBox.warning(
- self,
- QtGui.QApplication.translate(
+ reply = QtWidgets.QMessageBox.warning(
+ self, _translate("MessageView", "Follow external link"),
+ _translate(
"MessageView",
- "Follow external link"),
- QtGui.QApplication.translate(
- "MessageView",
- "The link \"%1\" will open in a browser. It may be a security risk, it could de-anonymise you"
- " or download malicious data. Are you sure?").arg(unicode(link.toString())),
- QtGui.QMessageBox.Yes,
- QtGui.QMessageBox.No)
- if reply == QtGui.QMessageBox.Yes:
+ "The link \"{0}\" will open in a browser. It may be"
+ " a security risk, it could de-anonymise you or download"
+ " malicious data. Are you sure?"
+ ).format(unic(ustr(link.toString()))),
+ QtWidgets.QMessageBox.Yes, QtWidgets.QMessageBox.No)
+ if reply == QtWidgets.QMessageBox.Yes:
QtGui.QDesktopServices.openUrl(link)
def loadResource(self, restype, name):
"""
- Callback for loading referenced objects, such as an image. For security reasons at the moment doesn't do
- anything)
+ Callback for loading referenced objects, such as an image.
+ For security reasons at the moment doesn't do anything
"""
pass
def lazyRender(self):
"""
- Partially render a message. This is to avoid UI freezing when loading huge messages. It continues loading as
- you scroll down.
+ Partially render a message. This is to avoid UI freezing when
+ loading huge messages. It continues loading as you scroll down.
"""
if self.rendering:
return
self.rendering = True
position = self.verticalScrollBar().value()
cursor = QtGui.QTextCursor(self.document())
- while self.outpos < len(self.out) and self.verticalScrollBar().value(
- ) >= self.document().size().height() - 2 * self.size().height():
+ while (
+ self.outpos < len(self.out)
+ and self.verticalScrollBar().value()
+ >= self.document().size().height() - 2 * self.size().height()
+ ):
startpos = self.outpos
self.outpos += 10240
# find next end of tag
@@ -123,8 +127,9 @@ class MessageView(QtGui.QTextBrowser):
pos = self.out.find(">", self.outpos)
if pos > self.outpos:
self.outpos = pos + 1
- cursor.movePosition(QtGui.QTextCursor.End, QtGui.QTextCursor.MoveAnchor)
- cursor.insertHtml(QtCore.QString(self.out[startpos:self.outpos]))
+ cursor.movePosition(
+ QtGui.QTextCursor.End, QtGui.QTextCursor.MoveAnchor)
+ cursor.insertHtml(unic(self.out[startpos:self.outpos]))
self.verticalScrollBar().setValue(position)
self.rendering = False
@@ -133,9 +138,11 @@ class MessageView(QtGui.QTextBrowser):
self.mode = MessageView.MODE_PLAIN
out = self.html.raw
if self.html.has_html:
- out = "
" + unicode(
- QtGui.QApplication.translate(
- "MessageView", "HTML detected, click here to display")) + "
" + out
+ out = (
+ '
'
+ + unic(ustr(_translate(
+ "MessageView", "HTML detected, click here to display"
+ ))) + '
' + out)
self.out = out
self.outpos = 0
self.setHtml("")
@@ -144,10 +151,10 @@ class MessageView(QtGui.QTextBrowser):
def showHTML(self):
"""Render message as HTML"""
self.mode = MessageView.MODE_HTML
- out = self.html.sanitised
- out = "
" + unicode(
- QtGui.QApplication.translate("MessageView", "Click here to disable HTML")) + "
" + out
- self.out = out
+ self.out = (
+ '
'
+ + _translate("MessageView", "Click here to disable HTML")
+ + '
' + self.html.sanitised)
self.outpos = 0
self.setHtml("")
self.lazyRender()
@@ -155,8 +162,6 @@ class MessageView(QtGui.QTextBrowser):
def setContent(self, data):
"""Set message content from argument"""
self.html = SafeHTMLParser()
- self.html.reset()
- self.html.reset_safe()
self.html.allow_picture = True
self.html.feed(data)
self.html.close()
diff --git a/src/bitmessageqt/migrationwizard.py b/src/bitmessageqt/migrationwizard.py
index 6e80f1dc..239770d2 100644
--- a/src/bitmessageqt/migrationwizard.py
+++ b/src/bitmessageqt/migrationwizard.py
@@ -1,16 +1,15 @@
-#!/usr/bin/env python2.7
-from PyQt4 import QtCore, QtGui
+from qtpy import QtCore, QtWidgets
-class MigrationWizardIntroPage(QtGui.QWizardPage):
+class MigrationWizardIntroPage(QtWidgets.QWizardPage):
def __init__(self):
- super(QtGui.QWizardPage, self).__init__()
+ super(QtWidgets.QWizardPage, self).__init__()
self.setTitle("Migrating configuration")
- label = QtGui.QLabel("This wizard will help you to migrate your configuration. "
+ label = QtWidgets.QLabel("This wizard will help you to migrate your configuration. "
"You can still keep using PyBitMessage once you migrate, the changes are backwards compatible.")
label.setWordWrap(True)
- layout = QtGui.QVBoxLayout()
+ layout = QtWidgets.QVBoxLayout()
layout.addWidget(label)
self.setLayout(layout)
@@ -18,15 +17,15 @@ class MigrationWizardIntroPage(QtGui.QWizardPage):
return 1
-class MigrationWizardAddressesPage(QtGui.QWizardPage):
+class MigrationWizardAddressesPage(QtWidgets.QWizardPage):
def __init__(self, addresses):
- super(QtGui.QWizardPage, self).__init__()
+ super(QtWidgets.QWizardPage, self).__init__()
self.setTitle("Addresses")
- label = QtGui.QLabel("Please select addresses that you are already using with mailchuck. ")
+ label = QtWidgets.QLabel("Please select addresses that you are already using with mailchuck. ")
label.setWordWrap(True)
- layout = QtGui.QVBoxLayout()
+ layout = QtWidgets.QVBoxLayout()
layout.addWidget(label)
self.setLayout(layout)
@@ -34,15 +33,15 @@ class MigrationWizardAddressesPage(QtGui.QWizardPage):
return 10
-class MigrationWizardGPUPage(QtGui.QWizardPage):
+class MigrationWizardGPUPage(QtWidgets.QWizardPage):
def __init__(self):
- super(QtGui.QWizardPage, self).__init__()
+ super(QtWidgets.QWizardPage, self).__init__()
self.setTitle("GPU")
- label = QtGui.QLabel("Are you using a GPU? ")
+ label = QtWidgets.QLabel("Are you using a GPU? ")
label.setWordWrap(True)
- layout = QtGui.QVBoxLayout()
+ layout = QtWidgets.QVBoxLayout()
layout.addWidget(label)
self.setLayout(layout)
@@ -50,22 +49,22 @@ class MigrationWizardGPUPage(QtGui.QWizardPage):
return 10
-class MigrationWizardConclusionPage(QtGui.QWizardPage):
+class MigrationWizardConclusionPage(QtWidgets.QWizardPage):
def __init__(self):
- super(QtGui.QWizardPage, self).__init__()
+ super(QtWidgets.QWizardPage, self).__init__()
self.setTitle("All done!")
- label = QtGui.QLabel("You successfully migrated.")
+ label = QtWidgets.QLabel("You successfully migrated.")
label.setWordWrap(True)
- layout = QtGui.QVBoxLayout()
+ layout = QtWidgets.QVBoxLayout()
layout.addWidget(label)
self.setLayout(layout)
-class Ui_MigrationWizard(QtGui.QWizard):
+class Ui_MigrationWizard(QtWidgets.QWizard):
def __init__(self, addresses):
- super(QtGui.QWizard, self).__init__()
+ super(QtWidgets.QWizard, self).__init__()
self.pages = {}
@@ -81,4 +80,4 @@ class Ui_MigrationWizard(QtGui.QWizard):
self.setWindowTitle("Migration from PyBitMessage wizard")
self.adjustSize()
- self.show()
\ No newline at end of file
+ self.show()
diff --git a/src/bitmessageqt/networkstatus.py b/src/bitmessageqt/networkstatus.py
index 5d669f39..b1d88535 100644
--- a/src/bitmessageqt/networkstatus.py
+++ b/src/bitmessageqt/networkstatus.py
@@ -4,26 +4,28 @@ Network status tab widget definition.
import time
-from PyQt4 import QtCore, QtGui
+from qtpy import QtCore, QtGui, QtWidgets
import l10n
import network.stats
import state
-import widgets
+from bitmessageqt import widgets
from network import connectionpool, knownnodes
-from retranslateui import RetranslateMixin
+from .retranslateui import RetranslateMixin
from tr import _translate
-from uisignaler import UISignaler
+from .uisignaler import UISignaler
-class NetworkStatus(QtGui.QWidget, RetranslateMixin):
+class NetworkStatus(QtWidgets.QWidget, RetranslateMixin):
"""Network status tab"""
def __init__(self, parent=None):
super(NetworkStatus, self).__init__(parent)
widgets.load('networkstatus.ui', self)
header = self.tableWidgetConnectionCount.horizontalHeader()
- header.setResizeMode(QtGui.QHeaderView.ResizeToContents)
+ header.setSectionResizeMode(QtWidgets.QHeaderView.ResizeToContents)
+ header.setSectionResizeMode(0, QtWidgets.QHeaderView.Stretch)
+ header.setSectionResizeMode(2, QtWidgets.QHeaderView.Stretch)
# Somehow this value was 5 when I tested
if header.sortIndicatorSection() > 4:
@@ -32,20 +34,17 @@ class NetworkStatus(QtGui.QWidget, RetranslateMixin):
self.startup = time.localtime()
self.UISignalThread = UISignaler.get()
- # pylint: disable=no-member
- QtCore.QObject.connect(self.UISignalThread, QtCore.SIGNAL(
- "updateNumberOfMessagesProcessed()"), self.updateNumberOfMessagesProcessed)
- QtCore.QObject.connect(self.UISignalThread, QtCore.SIGNAL(
- "updateNumberOfPubkeysProcessed()"), self.updateNumberOfPubkeysProcessed)
- QtCore.QObject.connect(self.UISignalThread, QtCore.SIGNAL(
- "updateNumberOfBroadcastsProcessed()"), self.updateNumberOfBroadcastsProcessed)
- QtCore.QObject.connect(self.UISignalThread, QtCore.SIGNAL(
- "updateNetworkStatusTab(PyQt_PyObject,PyQt_PyObject,PyQt_PyObject)"), self.updateNetworkStatusTab)
+ self.UISignalThread.updateNumberOfMessagesProcessed.connect(
+ self.updateNumberOfMessagesProcessed)
+ self.UISignalThread.updateNumberOfPubkeysProcessed.connect(
+ self.updateNumberOfPubkeysProcessed)
+ self.UISignalThread.updateNumberOfBroadcastsProcessed.connect(
+ self.updateNumberOfBroadcastsProcessed)
+ self.UISignalThread.updateNetworkStatusTab.connect(
+ self.updateNetworkStatusTab)
self.timer = QtCore.QTimer()
-
- QtCore.QObject.connect(self.timer, QtCore.SIGNAL("timeout()"), self.runEveryTwoSeconds)
- # pylint: enable=no-member
+ self.timer.timeout.connect(self.runEveryTwoSeconds)
def startUpdate(self):
"""Start a timer to update counters every 2 seconds"""
@@ -57,91 +56,66 @@ class NetworkStatus(QtGui.QWidget, RetranslateMixin):
"""Stop counter update timer"""
self.timer.stop()
- def formatBytes(self, num):
+ @staticmethod
+ def formatBytes(num):
"""Format bytes nicely (SI prefixes)"""
- # pylint: disable=no-self-use
- for x in [
- _translate(
- "networkstatus",
- "byte(s)",
- None,
- QtCore.QCoreApplication.CodecForTr,
- num),
- "kB",
- "MB",
- "GB",
- ]:
+ for x in (
+ _translate("networkstatus", "byte(s)", None, num),
+ "kB", "MB", "GB"
+ ):
if num < 1000.0:
return "%3.0f %s" % (num, x)
num /= 1000.0
- return "%3.0f %s" % (num, 'TB')
+ return "%3.0f %s" % (num, "TB")
- def formatByteRate(self, num):
+ @staticmethod
+ def formatByteRate(num):
"""Format transfer speed in kB/s"""
- # pylint: disable=no-self-use
num /= 1000
return "%4.0f kB" % num
def updateNumberOfObjectsToBeSynced(self):
"""Update the counter for number of objects to be synced"""
- self.labelSyncStatus.setText(
- _translate(
- "networkstatus",
- "Object(s) to be synced: %n",
- None,
- QtCore.QCoreApplication.CodecForTr,
- network.stats.pendingDownload()
- + network.stats.pendingUpload()))
+ self.labelSyncStatus.setText(_translate(
+ "networkstatus", "Object(s) to be synced: %n", None,
+ network.stats.pendingDownload() + network.stats.pendingUpload()))
def updateNumberOfMessagesProcessed(self):
"""Update the counter for number of processed messages"""
self.updateNumberOfObjectsToBeSynced()
- self.labelMessageCount.setText(
- _translate(
- "networkstatus",
- "Processed %n person-to-person message(s).",
- None,
- QtCore.QCoreApplication.CodecForTr,
- state.numberOfMessagesProcessed))
+ self.labelMessageCount.setText(_translate(
+ "networkstatus", "Processed %n person-to-person message(s).",
+ None, state.numberOfMessagesProcessed))
def updateNumberOfBroadcastsProcessed(self):
"""Update the counter for the number of processed broadcasts"""
self.updateNumberOfObjectsToBeSynced()
- self.labelBroadcastCount.setText(
- _translate(
- "networkstatus",
- "Processed %n broadcast message(s).",
- None,
- QtCore.QCoreApplication.CodecForTr,
- state.numberOfBroadcastsProcessed))
+ self.labelBroadcastCount.setText(_translate(
+ "networkstatus", "Processed %n broadcast message(s).", None,
+ state.numberOfBroadcastsProcessed))
def updateNumberOfPubkeysProcessed(self):
"""Update the counter for the number of processed pubkeys"""
self.updateNumberOfObjectsToBeSynced()
- self.labelPubkeyCount.setText(
- _translate(
- "networkstatus",
- "Processed %n public key(s).",
- None,
- QtCore.QCoreApplication.CodecForTr,
- state.numberOfPubkeysProcessed))
+ self.labelPubkeyCount.setText(_translate(
+ "networkstatus", "Processed %n public key(s).", None,
+ state.numberOfPubkeysProcessed))
def updateNumberOfBytes(self):
"""
- This function is run every two seconds, so we divide the rate of bytes
- sent and received by 2.
+ This function is run every two seconds, so we divide the rate
+ of bytes sent and received by 2.
"""
- self.labelBytesRecvCount.setText(
- _translate(
- "networkstatus",
- "Down: %1/s Total: %2").arg(
- self.formatByteRate(network.stats.downloadSpeed()),
- self.formatBytes(network.stats.receivedBytes())))
- self.labelBytesSentCount.setText(
- _translate(
- "networkstatus", "Up: %1/s Total: %2").arg(
- self.formatByteRate(network.stats.uploadSpeed()),
- self.formatBytes(network.stats.sentBytes())))
+ self.labelBytesRecvCount.setText(_translate(
+ "networkstatus", "Down: {0}/s Total: {1}").format(
+ self.formatByteRate(network.stats.downloadSpeed()),
+ self.formatBytes(network.stats.receivedBytes())
+ ))
+ self.labelBytesSentCount.setText(_translate(
+ "networkstatus", "Up: {0}/s Total: {1}").format(
+ self.formatByteRate(network.stats.uploadSpeed()),
+ self.formatBytes(network.stats.sentBytes())
+ ))
def updateNetworkStatusTab(self, outbound, add, destination):
"""Add or remove an entry to the list of connected peers"""
@@ -168,67 +142,67 @@ class NetworkStatus(QtGui.QWidget, RetranslateMixin):
if add:
self.tableWidgetConnectionCount.insertRow(0)
self.tableWidgetConnectionCount.setItem(
- 0, 0,
- QtGui.QTableWidgetItem("%s:%i" % (destination.host, destination.port))
- )
+ 0, 0, QtWidgets.QTableWidgetItem(
+ "%s:%i" % (destination.host, destination.port)))
self.tableWidgetConnectionCount.setItem(
- 0, 2,
- QtGui.QTableWidgetItem("%s" % (c.userAgent))
- )
+ 0, 2, QtWidgets.QTableWidgetItem("%s" % (c.userAgent.decode("utf-8", "replace"))))
self.tableWidgetConnectionCount.setItem(
- 0, 3,
- QtGui.QTableWidgetItem("%s" % (c.tlsVersion))
- )
+ 0, 3, QtWidgets.QTableWidgetItem("%s" % (c.tlsVersion)))
self.tableWidgetConnectionCount.setItem(
- 0, 4,
- QtGui.QTableWidgetItem("%s" % (",".join(map(str, c.streams))))
- )
+ 0, 4, QtWidgets.QTableWidgetItem(
+ "%s" % ",".join(map(str, c.streams))))
try:
# .. todo:: FIXME: hard coded stream no
- rating = "%.1f" % (knownnodes.knownNodes[1][destination]['rating'])
+ rating = "%.1f" % knownnodes.knownNodes[1][destination]['rating']
except KeyError:
rating = "-"
self.tableWidgetConnectionCount.setItem(
- 0, 1,
- QtGui.QTableWidgetItem("%s" % (rating))
- )
+ 0, 1, QtWidgets.QTableWidgetItem("%s" % (rating)))
if outbound:
- brush = QtGui.QBrush(QtGui.QColor("yellow"), QtCore.Qt.SolidPattern)
+ brush = QtGui.QBrush(
+ QtGui.QColor("yellow"), QtCore.Qt.SolidPattern)
else:
- brush = QtGui.QBrush(QtGui.QColor("green"), QtCore.Qt.SolidPattern)
+ brush = QtGui.QBrush(
+ QtGui.QColor("green"), QtCore.Qt.SolidPattern)
for j in range(1):
self.tableWidgetConnectionCount.item(0, j).setBackground(brush)
- self.tableWidgetConnectionCount.item(0, 0).setData(QtCore.Qt.UserRole, destination)
- self.tableWidgetConnectionCount.item(0, 1).setData(QtCore.Qt.UserRole, outbound)
+ self.tableWidgetConnectionCount.item(0, 0).setData(
+ QtCore.Qt.UserRole, destination)
+ self.tableWidgetConnectionCount.item(0, 1).setData(
+ QtCore.Qt.UserRole, outbound)
else:
if not connectionpool.pool.inboundConnections:
self.window().setStatusIcon('yellow')
for i in range(self.tableWidgetConnectionCount.rowCount()):
- if self.tableWidgetConnectionCount.item(i, 0).data(QtCore.Qt.UserRole).toPyObject() != destination:
+ if self.tableWidgetConnectionCount.item(i, 0).data(
+ QtCore.Qt.UserRole) != destination:
continue
- if self.tableWidgetConnectionCount.item(i, 1).data(QtCore.Qt.UserRole).toPyObject() == outbound:
+ if self.tableWidgetConnectionCount.item(i, 1).data(
+ QtCore.Qt.UserRole) == outbound:
self.tableWidgetConnectionCount.removeRow(i)
break
self.tableWidgetConnectionCount.setUpdatesEnabled(True)
self.tableWidgetConnectionCount.setSortingEnabled(True)
- self.labelTotalConnections.setText(
- _translate(
- "networkstatus", "Total Connections: %1").arg(
- str(self.tableWidgetConnectionCount.rowCount())))
- # FYI: The 'singlelistener' thread sets the icon color to green when it
- # receives an incoming connection, meaning that the user's firewall is
- # configured correctly.
- if self.tableWidgetConnectionCount.rowCount() and state.statusIconColor == 'red':
- self.window().setStatusIcon('yellow')
- elif self.tableWidgetConnectionCount.rowCount() == 0 and state.statusIconColor != "red":
+ self.labelTotalConnections.setText(_translate(
+ "networkstatus", "Total Connections: {0}").format(
+ self.tableWidgetConnectionCount.rowCount()
+ ))
+ # FYI: The 'singlelistener' thread sets the icon color to green
+ # when it receives an incoming connection, meaning that the user's
+ # firewall is configured correctly.
+ if self.tableWidgetConnectionCount.rowCount():
+ if state.statusIconColor == 'red':
+ self.window().setStatusIcon('yellow')
+ elif state.statusIconColor != 'red':
self.window().setStatusIcon('red')
# timer driven
def runEveryTwoSeconds(self):
"""Updates counters, runs every 2 seconds if the timer is running"""
- self.labelLookupsPerSecond.setText(_translate("networkstatus", "Inventory lookups per second: %1").arg(
- str(state.Inventory.numberOfInventoryLookupsPerformed / 2)))
+ self.labelLookupsPerSecond.setText(_translate(
+ "networkstatus", "Inventory lookups per second: {0}"
+ ).format(state.Inventory.numberOfInventoryLookupsPerformed / 2))
state.Inventory.numberOfInventoryLookupsPerformed = 0
self.updateNumberOfBytes()
self.updateNumberOfObjectsToBeSynced()
@@ -236,13 +210,12 @@ class NetworkStatus(QtGui.QWidget, RetranslateMixin):
def retranslateUi(self):
"""Conventional Qt Designer method for dynamic l10n"""
super(NetworkStatus, self).retranslateUi()
- self.labelTotalConnections.setText(
- _translate(
- "networkstatus", "Total Connections: %1").arg(
- str(self.tableWidgetConnectionCount.rowCount())))
+ self.labelTotalConnections.setText(_translate(
+ "networkstatus", "Total Connections: {0}"
+ ).format(self.tableWidgetConnectionCount.rowCount()))
self.labelStartupTime.setText(_translate(
- "networkstatus", "Since startup on %1"
- ).arg(l10n.formatTimestamp(self.startup)))
+ "networkstatus", "Since startup on {0}"
+ ).format(l10n.formatTimestamp(self.startup)))
self.updateNumberOfMessagesProcessed()
self.updateNumberOfBroadcastsProcessed()
self.updateNumberOfPubkeysProcessed()
diff --git a/src/bitmessageqt/networkstatus.ui b/src/bitmessageqt/networkstatus.ui
index e0c01b57..7830714a 100644
--- a/src/bitmessageqt/networkstatus.ui
+++ b/src/bitmessageqt/networkstatus.ui
@@ -100,6 +100,9 @@
true
+
+ false
+ true
@@ -109,9 +112,6 @@
false
-
- true
- false
@@ -296,11 +296,8 @@
STableWidgetQTableWidget
- bitmessageqt/settingsmixin.h
+ bitmessageqt.settingsmixin
-
-
-
diff --git a/src/bitmessageqt/newaddressdialog.ui b/src/bitmessageqt/newaddressdialog.ui
index 8b5276cc..8a7cc6ae 100644
--- a/src/bitmessageqt/newaddressdialog.ui
+++ b/src/bitmessageqt/newaddressdialog.ui
@@ -375,7 +375,7 @@ The 'Random Number' option is selected by default but deterministic addresses ha
radioButtonDeterministicAddresstoggled(bool)groupBoxDeterministic
- setShown(bool)
+ setVisible(bool)92
@@ -391,7 +391,7 @@ The 'Random Number' option is selected by default but deterministic addresses ha
radioButtonRandomAddresstoggled(bool)groupBox
- setShown(bool)
+ setVisible(bool)72
diff --git a/src/bitmessageqt/newchandialog.py b/src/bitmessageqt/newchandialog.py
index c0629cd7..bbb11d7d 100644
--- a/src/bitmessageqt/newchandialog.py
+++ b/src/bitmessageqt/newchandialog.py
@@ -1,44 +1,34 @@
"""
-src/bitmessageqt/newchandialog.py
-=================================
-
+NewChanDialog class definition
"""
-from PyQt4 import QtCore, QtGui
+from unqstr import ustr, unic
+from qtpy import QtCore, QtWidgets
-import widgets
+from bitmessageqt import widgets
from addresses import addBMIfNotPresent
-from addressvalidator import AddressValidator, PassPhraseValidator
+from .addressvalidator import AddressValidator, PassPhraseValidator
from queues import (
addressGeneratorQueue, apiAddressGeneratorReturnQueue, UISignalQueue)
from tr import _translate
-from utils import str_chan
+from .utils import str_chan
-class NewChanDialog(QtGui.QDialog):
- """The `New Chan` dialog"""
+class NewChanDialog(QtWidgets.QDialog):
+ """The "New Chan" dialog"""
def __init__(self, parent=None):
super(NewChanDialog, self).__init__(parent)
widgets.load('newchandialog.ui', self)
self.parent = parent
- self.chanAddress.setValidator(
- AddressValidator(
- self.chanAddress,
- self.chanPassPhrase,
- self.validatorFeedback,
- self.buttonBox,
- False))
- self.chanPassPhrase.setValidator(
- PassPhraseValidator(
- self.chanPassPhrase,
- self.chanAddress,
- self.validatorFeedback,
- self.buttonBox,
- False))
+ self.chanAddress.setValidator(AddressValidator(
+ self.chanAddress, self.chanPassPhrase, self.validatorFeedback,
+ self.buttonBox.button(QtWidgets.QDialogButtonBox.Ok), False))
+ self.chanPassPhrase.setValidator(PassPhraseValidator(
+ self.chanPassPhrase, self.chanAddress, self.validatorFeedback,
+ self.buttonBox.button(QtWidgets.QDialogButtonBox.Ok), False))
self.timer = QtCore.QTimer()
- QtCore.QObject.connect( # pylint: disable=no-member
- self.timer, QtCore.SIGNAL("timeout()"), self.delayedUpdateStatus)
+ self.timer.timeout.connect(self.delayedUpdateStatus)
self.timer.start(500) # milliseconds
self.setAttribute(QtCore.Qt.WA_DeleteOnClose)
self.show()
@@ -52,32 +42,38 @@ class NewChanDialog(QtGui.QDialog):
self.timer.stop()
self.hide()
apiAddressGeneratorReturnQueue.queue.clear()
- if self.chanAddress.text().toUtf8() == "":
+ if ustr(self.chanAddress.text()) == "":
addressGeneratorQueue.put(
- ('createChan', 4, 1, str_chan + ' ' + str(self.chanPassPhrase.text().toUtf8()),
- self.chanPassPhrase.text().toUtf8(),
+ ('createChan', 4, 1, str_chan + ' ' + ustr(self.chanPassPhrase.text()),
+ ustr(self.chanPassPhrase.text()).encode("utf-8", "replace"),
True))
else:
addressGeneratorQueue.put(
- ('joinChan', addBMIfNotPresent(self.chanAddress.text().toUtf8()),
- str_chan + ' ' + str(self.chanPassPhrase.text().toUtf8()),
- self.chanPassPhrase.text().toUtf8(),
+ ('joinChan', addBMIfNotPresent(ustr(self.chanAddress.text())),
+ str_chan + ' ' + ustr(self.chanPassPhrase.text()),
+ ustr(self.chanPassPhrase.text()).encode("utf-8", "replace"),
True))
addressGeneratorReturnValue = apiAddressGeneratorReturnQueue.get(True)
- if addressGeneratorReturnValue and addressGeneratorReturnValue[0] != 'chan name does not match address':
+ if len(addressGeneratorReturnValue) > 0 and addressGeneratorReturnValue[0] != 'chan name does not match address':
UISignalQueue.put(('updateStatusBar', _translate(
- "newchandialog", "Successfully created / joined chan %1").arg(unicode(self.chanPassPhrase.text()))))
+ "newchandialog", "Successfully created / joined chan {0}").format(unic(ustr(self.chanPassPhrase.text())))))
self.parent.ui.tabWidget.setCurrentIndex(
self.parent.ui.tabWidget.indexOf(self.parent.ui.chans)
)
- self.done(QtGui.QDialog.Accepted)
+ self.done(QtWidgets.QDialog.Accepted)
else:
- UISignalQueue.put(('updateStatusBar', _translate("newchandialog", "Chan creation / joining failed")))
- self.done(QtGui.QDialog.Rejected)
+ UISignalQueue.put((
+ 'updateStatusBar',
+ _translate("newchandialog", "Chan creation / joining failed")
+ ))
+ self.done(QtWidgets.QDialog.Rejected)
def reject(self):
"""Cancel joining the chan"""
self.timer.stop()
self.hide()
- UISignalQueue.put(('updateStatusBar', _translate("newchandialog", "Chan creation / joining cancelled")))
- self.done(QtGui.QDialog.Rejected)
+ UISignalQueue.put((
+ 'updateStatusBar',
+ _translate("newchandialog", "Chan creation / joining cancelled")
+ ))
+ self.done(QtWidgets.QDialog.Rejected)
diff --git a/src/bitmessageqt/retranslateui.py b/src/bitmessageqt/retranslateui.py
index c7676f77..8a2ec860 100644
--- a/src/bitmessageqt/retranslateui.py
+++ b/src/bitmessageqt/retranslateui.py
@@ -1,20 +1,21 @@
-from os import path
-from PyQt4 import QtGui
-from debug import logger
-import widgets
+from unqstr import ustr
+import six
+from bitmessageqt import widgets
+from qtpy import QtWidgets
+
class RetranslateMixin(object):
def retranslateUi(self):
- defaults = QtGui.QWidget()
+ defaults = QtWidgets.QWidget()
widgets.load(self.__class__.__name__.lower() + '.ui', defaults)
- for attr, value in defaults.__dict__.iteritems():
+ for attr, value in six.iteritems(defaults.__dict__):
setTextMethod = getattr(value, "setText", None)
if callable(setTextMethod):
- getattr(self, attr).setText(getattr(defaults, attr).text())
- elif isinstance(value, QtGui.QTableWidget):
- for i in range (value.columnCount()):
+ getattr(self, attr).setText(ustr(getattr(defaults, attr).text()))
+ elif isinstance(value, QtWidgets.QTableWidget):
+ for i in range(value.columnCount()):
getattr(self, attr).horizontalHeaderItem(i).setText(
- getattr(defaults, attr).horizontalHeaderItem(i).text())
- for i in range (value.rowCount()):
+ ustr(getattr(defaults, attr).horizontalHeaderItem(i).text()))
+ for i in range(value.rowCount()):
getattr(self, attr).verticalHeaderItem(i).setText(
- getattr(defaults, attr).verticalHeaderItem(i).text())
+ ustr(getattr(defaults, attr).verticalHeaderItem(i).text()))
diff --git a/src/bitmessageqt/safehtmlparser.py b/src/bitmessageqt/safehtmlparser.py
index d408d2c7..5731f434 100644
--- a/src/bitmessageqt/safehtmlparser.py
+++ b/src/bitmessageqt/safehtmlparser.py
@@ -2,11 +2,11 @@
import inspect
import re
-from HTMLParser import HTMLParser
+from six.moves.html_parser import HTMLParser
-from urllib import quote_plus
-from urlparse import urlparse
+from six.moves.urllib.parse import quote_plus, urlparse
+from unqstr import ustr, unic
class SafeHTMLParser(HTMLParser):
"""HTML parser with sanitisation"""
@@ -124,9 +124,9 @@ class SafeHTMLParser(HTMLParser):
def feed(self, data):
try:
- data = unicode(data, 'utf-8')
- except UnicodeDecodeError:
- data = unicode(data, 'utf-8', errors='replace')
+ data = unic(ustr(data))
+ except TypeError:
+ pass
HTMLParser.feed(self, data)
tmp = SafeHTMLParser.replace_pre(data)
tmp = self.uriregex1.sub(r'\1', tmp)
diff --git a/src/bitmessageqt/settings.py b/src/bitmessageqt/settings.py
index eeb507c7..d54a0f68 100644
--- a/src/bitmessageqt/settings.py
+++ b/src/bitmessageqt/settings.py
@@ -1,13 +1,14 @@
"""
-This module setting file is for settings
+SettingsDialog class definition
"""
-import ConfigParser
+from six.moves import configparser
import os
import sys
import tempfile
+from qtpy import QtCore, QtGui, QtWidgets
import six
-from PyQt4 import QtCore, QtGui
+from unqstr import ustr
import debug
import defaults
@@ -16,7 +17,7 @@ import openclpow
import paths
import queues
import state
-import widgets
+from bitmessageqt import widgets
from bmconfigparser import config as config_obj
from helper_sql import sqlExecute, sqlStoredProcedure
from helper_startup import start_proxyconfig
@@ -26,12 +27,18 @@ from network.asyncore_pollchoose import set_rates
from tr import _translate
+try:
+ SafeConfigParser = configparser.SafeConfigParser
+except AttributeError:
+ # alpine linux, python3.12
+ SafeConfigParser = configparser.ConfigParser
+
def getSOCKSProxyType(config):
"""Get user socksproxytype setting from *config*"""
try:
- result = ConfigParser.SafeConfigParser.get(
+ result = SafeConfigParser.get(
config, 'bitmessagesettings', 'socksproxytype')
- except (ConfigParser.NoSectionError, ConfigParser.NoOptionError):
+ except (configparser.NoSectionError, configparser.NoOptionError):
return None
else:
if result.lower() in ('', 'none', 'false'):
@@ -39,14 +46,14 @@ def getSOCKSProxyType(config):
return result
-class SettingsDialog(QtGui.QDialog):
+class SettingsDialog(QtWidgets.QDialog):
"""The "Settings" dialog"""
# pylint: disable=too-many-instance-attributes
def __init__(self, parent=None, firstrun=False):
super(SettingsDialog, self).__init__(parent)
widgets.load('settings.ui', self)
- self.app = QtGui.QApplication.instance()
+ self.app = QtWidgets.QApplication.instance()
self.parent = parent
self.firstrun = firstrun
self.config = config_obj
@@ -83,14 +90,14 @@ class SettingsDialog(QtGui.QDialog):
self.tabWidgetSettings.setCurrentIndex(
self.tabWidgetSettings.indexOf(self.tabNetworkSettings)
)
- QtGui.QWidget.resize(self, QtGui.QWidget.sizeHint(self))
+ QtWidgets.QWidget.resize(self, QtWidgets.QWidget.sizeHint(self))
def adjust_from_config(self, config):
"""Adjust all widgets state according to config settings"""
# pylint: disable=too-many-branches,too-many-statements
current_style = self.app.get_windowstyle()
- for i, sk in enumerate(QtGui.QStyleFactory.keys()):
+ for i, sk in enumerate(QtWidgets.QStyleFactory.keys()):
self.comboBoxStyle.addItem(sk)
if sk == current_style:
self.comboBoxStyle.setCurrentIndex(i)
@@ -187,7 +194,7 @@ class SettingsDialog(QtGui.QDialog):
else:
if self.checkBoxOnionOnly.isChecked():
self.checkBoxOnionOnly.setText(
- self.checkBoxOnionOnly.text() + ", " + _translate(
+ ustr(self.checkBoxOnionOnly.text()) + ", " + _translate(
"MainWindow", "may cause connection problems!"))
self.checkBoxOnionOnly.setStyleSheet(
"QCheckBox { color : red; }")
@@ -324,10 +331,10 @@ class SettingsDialog(QtGui.QDialog):
_translate("MainWindow", "Testing..."))
nc = namecoin.namecoinConnection({
'type': self.getNamecoinType(),
- 'host': str(self.lineEditNamecoinHost.text().toUtf8()),
- 'port': str(self.lineEditNamecoinPort.text().toUtf8()),
- 'user': str(self.lineEditNamecoinUser.text().toUtf8()),
- 'password': str(self.lineEditNamecoinPassword.text().toUtf8())
+ 'host': ustr(self.lineEditNamecoinHost.text()),
+ 'port': ustr(self.lineEditNamecoinPort.text()),
+ 'user': ustr(self.lineEditNamecoinUser.text()),
+ 'password': ustr(self.lineEditNamecoinPassword.text())
})
status, text = nc.test()
self.labelNamecoinTestResult.setText(text)
@@ -342,7 +349,7 @@ class SettingsDialog(QtGui.QDialog):
def choose_font(self):
"""Show the font selection dialog"""
- font, valid = QtGui.QFontDialog.getFont()
+ font, valid = QtWidgets.QFontDialog.getFont()
if valid:
self.save_font_setting(font)
@@ -372,7 +379,7 @@ class SettingsDialog(QtGui.QDialog):
self.config.set('bitmessagesettings', 'replybelow', str(
self.checkBoxReplyBelow.isChecked()))
- window_style = str(self.comboBoxStyle.currentText())
+ window_style = ustr(self.comboBoxStyle.currentText())
if self.app.get_windowstyle() != window_style or self.config.safeGet(
'bitmessagesettings', 'font'
) != self.font_setting:
@@ -386,8 +393,8 @@ class SettingsDialog(QtGui.QDialog):
" the window style or default font."), 1)
))
- lang = str(self.languageComboBox.itemData(
- self.languageComboBox.currentIndex()).toString())
+ lang = ustr(self.languageComboBox.itemData(
+ self.languageComboBox.currentIndex()))
self.config.set('bitmessagesettings', 'userlocale', lang)
self.parent.change_translation()
@@ -469,7 +476,7 @@ class SettingsDialog(QtGui.QDialog):
self.config.set('bitmessagesettings', 'maxuploadrate', str(
int(float(self.lineEditMaxUploadRate.text()))))
except ValueError:
- QtGui.QMessageBox.about(
+ QtWidgets.QMessageBox.about(
self, _translate("MainWindow", "Number needed"),
_translate(
"MainWindow",
@@ -486,13 +493,13 @@ class SettingsDialog(QtGui.QDialog):
self.config.set(
'bitmessagesettings', 'namecoinrpctype', self.getNamecoinType())
- self.config.set('bitmessagesettings', 'namecoinrpchost', str(
+ self.config.set('bitmessagesettings', 'namecoinrpchost', ustr(
self.lineEditNamecoinHost.text()))
- self.config.set('bitmessagesettings', 'namecoinrpcport', str(
+ self.config.set('bitmessagesettings', 'namecoinrpcport', ustr(
self.lineEditNamecoinPort.text()))
- self.config.set('bitmessagesettings', 'namecoinrpcuser', str(
+ self.config.set('bitmessagesettings', 'namecoinrpcuser', ustr(
self.lineEditNamecoinUser.text()))
- self.config.set('bitmessagesettings', 'namecoinrpcpassword', str(
+ self.config.set('bitmessagesettings', 'namecoinrpcpassword', ustr(
self.lineEditNamecoinPassword.text()))
self.parent.resetNamecoinConnection()
@@ -510,11 +517,11 @@ class SettingsDialog(QtGui.QDialog):
float(self.lineEditSmallMessageDifficulty.text())
* defaults.networkDefaultPayloadLengthExtraBytes)))
- if self.comboBoxOpenCL.currentText().toUtf8() != self.config.safeGet(
- 'bitmessagesettings', 'opencl'):
+ if ustr(self.comboBoxOpenCL.currentText()) != ustr(self.config.safeGet(
+ 'bitmessagesettings', 'opencl')):
self.config.set(
'bitmessagesettings', 'opencl',
- str(self.comboBoxOpenCL.currentText()))
+ ustr(self.comboBoxOpenCL.currentText()))
queues.workerQueue.put(('resetPoW', ''))
acceptableDifficultyChanged = False
@@ -524,7 +531,7 @@ class SettingsDialog(QtGui.QDialog):
or float(self.lineEditMaxAcceptableTotalDifficulty.text()) == 0
):
if self.config.get(
- 'bitmessagesettings', 'maxacceptablenoncetrialsperbyte'
+ 'bitmessagesettings', 'maxacceptablenoncetrialsperbyte'
) != str(int(
float(self.lineEditMaxAcceptableTotalDifficulty.text())
* defaults.networkDefaultProofOfWorkNonceTrialsPerByte)):
@@ -541,7 +548,7 @@ class SettingsDialog(QtGui.QDialog):
or float(self.lineEditMaxAcceptableSmallMessageDifficulty.text()) == 0
):
if self.config.get(
- 'bitmessagesettings', 'maxacceptablepayloadlengthextrabytes'
+ 'bitmessagesettings', 'maxacceptablepayloadlengthextrabytes'
) != str(int(
float(self.lineEditMaxAcceptableSmallMessageDifficulty.text())
* defaults.networkDefaultPayloadLengthExtraBytes)):
@@ -593,7 +600,7 @@ class SettingsDialog(QtGui.QDialog):
if state.maximumLengthOfTimeToBotherResendingMessages < 432000:
# If the time period is less than 5 hours, we give
# zero values to all fields. No message will be sent again.
- QtGui.QMessageBox.about(
+ QtWidgets.QMessageBox.about(
self,
_translate("MainWindow", "Will not resend ever"),
_translate(
diff --git a/src/bitmessageqt/settings.ui b/src/bitmessageqt/settings.ui
index e7ce1d71..cdad7ba5 100644
--- a/src/bitmessageqt/settings.ui
+++ b/src/bitmessageqt/settings.ui
@@ -1112,9 +1112,6 @@
checkBoxSocksListenbuttonBox
-
-
- buttonBox
diff --git a/src/bitmessageqt/settingsmixin.py b/src/bitmessageqt/settingsmixin.py
index 3d5999e2..94c25615 100644
--- a/src/bitmessageqt/settingsmixin.py
+++ b/src/bitmessageqt/settingsmixin.py
@@ -1,15 +1,16 @@
-#!/usr/bin/python2.7
"""
src/settingsmixin.py
====================
"""
-from PyQt4 import QtCore, QtGui
+from unqstr import ustr
+from qtpy import QtCore, QtWidgets
class SettingsMixin(object):
- """Mixin for adding geometry and state saving between restarts."""
+ """Mixin for adding geometry and state saving between restarts"""
+
def warnIfNoObjectName(self):
"""
Handle objects which don't have a name. Currently it ignores them. Objects without a name can't have their
@@ -40,8 +41,9 @@ class SettingsMixin(object):
self.warnIfNoObjectName()
settings = QtCore.QSettings()
try:
- geom = settings.value("/".join([str(self.objectName()), "geometry"]))
- target.restoreGeometry(geom.toByteArray() if hasattr(geom, 'toByteArray') else geom)
+ geom = settings.value(
+ "/".join([ustr(self.objectName()), "geometry"]))
+ target.restoreGeometry(geom)
except Exception:
pass
@@ -50,14 +52,15 @@ class SettingsMixin(object):
self.warnIfNoObjectName()
settings = QtCore.QSettings()
try:
- state = settings.value("/".join([str(self.objectName()), "state"]))
- target.restoreState(state.toByteArray() if hasattr(state, 'toByteArray') else state)
+ state = settings.value("/".join([ustr(self.objectName()), "state"]))
+ target.restoreState(state)
except Exception:
pass
-class SMainWindow(QtGui.QMainWindow, SettingsMixin):
- """Main window with Settings functionality."""
+class SMainWindow(QtWidgets.QMainWindow, SettingsMixin):
+ """Main window with Settings functionality"""
+
def loadSettings(self):
"""Load main window settings."""
self.readGeometry(self)
@@ -69,9 +72,9 @@ class SMainWindow(QtGui.QMainWindow, SettingsMixin):
self.writeGeometry(self)
-class STableWidget(QtGui.QTableWidget, SettingsMixin):
+class STableWidget(QtWidgets.QTableWidget, SettingsMixin):
"""Table widget with Settings functionality"""
- # pylint: disable=too-many-ancestors
+
def loadSettings(self):
"""Load table settings."""
self.readState(self.horizontalHeader())
@@ -81,8 +84,9 @@ class STableWidget(QtGui.QTableWidget, SettingsMixin):
self.writeState(self.horizontalHeader())
-class SSplitter(QtGui.QSplitter, SettingsMixin):
- """Splitter with Settings functionality."""
+class SSplitter(QtWidgets.QSplitter, SettingsMixin):
+ """Splitter with Settings functionality"""
+
def loadSettings(self):
"""Load splitter settings"""
self.readState(self)
@@ -92,17 +96,17 @@ class SSplitter(QtGui.QSplitter, SettingsMixin):
self.writeState(self)
-class STreeWidget(QtGui.QTreeWidget, SettingsMixin):
- """Tree widget with settings functionality."""
- # pylint: disable=too-many-ancestors
+class STreeWidget(QtWidgets.QTreeWidget, SettingsMixin):
+ """Tree widget with settings functionality"""
+
def loadSettings(self):
- """Load tree settings."""
+ """Load tree settings. Unimplemented."""
# recurse children
# self.readState(self)
pass
def saveSettings(self):
- """Save tree settings"""
+ """Save tree settings. Unimplemented."""
# recurse children
# self.writeState(self)
pass
diff --git a/src/bitmessageqt/statusbar.py b/src/bitmessageqt/statusbar.py
index 2add604d..478d570c 100644
--- a/src/bitmessageqt/statusbar.py
+++ b/src/bitmessageqt/statusbar.py
@@ -1,11 +1,11 @@
-# pylint: disable=unused-argument
-"""Status bar Module"""
+"""BMStatusBar class definition"""
from time import time
-from PyQt4 import QtGui
+
+from qtpy import QtWidgets
-class BMStatusBar(QtGui.QStatusBar):
+class BMStatusBar(QtWidgets.QStatusBar):
"""Status bar with queue and priorities"""
duration = 10000
deleteAfter = 60
@@ -16,21 +16,24 @@ class BMStatusBar(QtGui.QStatusBar):
self.timer = self.startTimer(BMStatusBar.duration)
self.iterator = 0
- def timerEvent(self, event):
+ def timerEvent(self, event): # pylint: disable=unused-argument
"""an event handler which allows to queue and prioritise messages to
show in the status bar, for example if many messages come very quickly
after one another, it adds delays and so on"""
while len(self.important) > 0:
self.iterator += 1
try:
- if time() > self.important[self.iterator][1] + BMStatusBar.deleteAfter:
+ if (
+ self.important[self.iterator][1]
+ + BMStatusBar.deleteAfter < time()
+ ):
del self.important[self.iterator]
self.iterator -= 1
continue
except IndexError:
self.iterator = -1
continue
- super(BMStatusBar, self).showMessage(self.important[self.iterator][0], 0)
+ self.showMessage(self.important[self.iterator][0], 0)
break
def addImportant(self, message):
diff --git a/src/bitmessageqt/support.py b/src/bitmessageqt/support.py
index a84affa4..dae2fe4c 100644
--- a/src/bitmessageqt/support.py
+++ b/src/bitmessageqt/support.py
@@ -1,14 +1,15 @@
"""Composing support request message functions."""
-# pylint: disable=no-member
import ctypes
+import os
import ssl
import sys
import time
-from PyQt4 import QtCore
+from unqstr import ustr, unic
+from dbcompat import dbstr
-import account
+from bitmessageqt import account
import defaults
import network.stats
import paths
@@ -16,12 +17,12 @@ import proofofwork
import queues
import state
from bmconfigparser import config
-from foldertree import AccountMixin
+from .foldertree import AccountMixin
from helper_sql import sqlExecute, sqlQuery
from l10n import getTranslationLanguage
from openclpow import openclEnabled
from pyelliptic.openssl import OpenSSL
-from settings import getSOCKSProxyType
+from .settings import getSOCKSProxyType
from version import softwareVersion
from tr import _translate
@@ -31,7 +32,7 @@ OLD_SUPPORT_ADDRESS = 'BM-2cTkCtMYkrSPwFTpgcBrMrf5d8oZwvMZWK'
SUPPORT_ADDRESS = 'BM-2cUdgkDDAahwPAU6oD2A7DnjqZz3hgY832'
SUPPORT_LABEL = _translate("Support", "PyBitmessage support")
SUPPORT_MY_LABEL = _translate("Support", "My new address")
-SUPPORT_SUBJECT = 'Support request'
+SUPPORT_SUBJECT = _translate("Support", "Support request")
SUPPORT_MESSAGE = _translate("Support", '''
You can use this message to send a report to one of the PyBitmessage core \
developers regarding PyBitmessage or the mailchuck.com email service. \
@@ -55,6 +56,7 @@ Operating system: {}
Architecture: {}bit
Python Version: {}
OpenSSL Version: {}
+Qt API: {}
Frozen: {}
Portable mode: {}
C PoW: {}
@@ -67,28 +69,35 @@ Connected hosts: {}
def checkAddressBook(myapp):
- sqlExecute('DELETE from addressbook WHERE address=?', OLD_SUPPORT_ADDRESS)
- queryreturn = sqlQuery('SELECT * FROM addressbook WHERE address=?', SUPPORT_ADDRESS)
+ """
+ Add "PyBitmessage support" address to address book, remove old one if found.
+ """
+ sqlExecute('DELETE from addressbook WHERE address=?', dbstr(OLD_SUPPORT_ADDRESS))
+ queryreturn = sqlQuery(
+ 'SELECT * FROM addressbook WHERE address=?', dbstr(SUPPORT_ADDRESS))
if queryreturn == []:
sqlExecute(
'INSERT INTO addressbook VALUES (?,?)',
- SUPPORT_LABEL.toUtf8(), SUPPORT_ADDRESS)
+ dbstr(SUPPORT_LABEL), dbstr(SUPPORT_ADDRESS))
myapp.rerenderAddressBook()
def checkHasNormalAddress():
- for address in config.addresses():
+ """Returns first enabled normal address or False if not found."""
+ for address in config.addresses(True):
acct = account.accountClass(address)
- if acct.type == AccountMixin.NORMAL and config.safeGetBoolean(address, 'enabled'):
+ if acct.type == AccountMixin.NORMAL and config.safeGetBoolean(
+ address, 'enabled'):
return address
return False
def createAddressIfNeeded(myapp):
+ """Checks if user has any anabled normal address, creates new one if no."""
if not checkHasNormalAddress():
queues.addressGeneratorQueue.put((
'createRandomAddress', 4, 1,
- str(SUPPORT_MY_LABEL.toUtf8()),
+ ustr(SUPPORT_MY_LABEL),
1, "", False,
defaults.networkDefaultProofOfWorkNonceTrialsPerByte,
defaults.networkDefaultPayloadLengthExtraBytes
@@ -100,15 +109,20 @@ def createAddressIfNeeded(myapp):
def createSupportMessage(myapp):
+ """
+ Prepare the support request message and switch to tab "Send"
+ """
checkAddressBook(myapp)
address = createAddressIfNeeded(myapp)
if state.shutdown:
return
myapp.ui.lineEditSubject.setText(SUPPORT_SUBJECT)
- addrIndex = myapp.ui.comboBoxSendFrom.findData(
- address, QtCore.Qt.UserRole,
- QtCore.Qt.MatchFixedString | QtCore.Qt.MatchCaseSensitive)
+ # addrIndex = myapp.ui.comboBoxSendFrom.findData(
+ # address, QtCore.Qt.UserRole,
+ # QtCore.Qt.MatchFixedString | QtCore.Qt.MatchCaseSensitive
+ # )
+ addrIndex = myapp.ui.comboBoxSendFrom.findData(address)
if addrIndex == -1: # something is very wrong
return
myapp.ui.comboBoxSendFrom.setCurrentIndex(addrIndex)
@@ -119,15 +133,13 @@ def createSupportMessage(myapp):
if commit:
version += " GIT " + commit
- os = sys.platform
- if os == "win32":
- windowsversion = sys.getwindowsversion()
- os = "Windows " + str(windowsversion[0]) + "." + str(windowsversion[1])
+ if sys.platform.startswith("win"):
+ # pylint: disable=no-member
+ osname = "Windows %s.%s" % sys.getwindowsversion()[:2]
else:
try:
- from os import uname
- unixversion = uname()
- os = unixversion[0] + " " + unixversion[2]
+ unixversion = os.uname()
+ osname = unixversion[0] + " " + unixversion[2]
except:
pass
architecture = "32" if ctypes.sizeof(ctypes.c_voidp) == 4 else "64"
@@ -136,22 +148,26 @@ def createSupportMessage(myapp):
opensslversion = "%s (Python internal), %s (external for PyElliptic)" % (
ssl.OPENSSL_VERSION, OpenSSL._version)
+ qtapi = os.environ.get('QT_API', 'fallback')
+
frozen = "N/A"
if paths.frozen:
frozen = paths.frozen
- portablemode = "True" if state.appdata == paths.lookupExeFolder() else "False"
+ portablemode = str(state.appdata == paths.lookupExeFolder())
cpow = "True" if proofofwork.bmpow else "False"
- openclpow = str(
+ openclpow = ustr(
config.safeGet('bitmessagesettings', 'opencl')
) if openclEnabled() else "None"
locale = getTranslationLanguage()
- socks = getSOCKSProxyType(config) or "N/A"
- upnp = config.safeGet('bitmessagesettings', 'upnp', "N/A")
+ socks = getSOCKSProxyType(config) or 'N/A'
+ upnp = config.safeGet('bitmessagesettings', 'upnp', 'N/A')
connectedhosts = len(network.stats.connectedHostsList())
- myapp.ui.textEditMessage.setText(unicode(SUPPORT_MESSAGE, 'utf-8').format(
- version, os, architecture, pythonversion, opensslversion, frozen,
- portablemode, cpow, openclpow, locale, socks, upnp, connectedhosts))
+ myapp.ui.textEditMessage.setText(unic(ustr(SUPPORT_MESSAGE).format(
+ version, osname, architecture, pythonversion, opensslversion, qtapi,
+ frozen, portablemode, cpow, openclpow, locale, socks, upnp,
+ connectedhosts
+ )))
# single msg tab
myapp.ui.tabWidgetSend.setCurrentIndex(
diff --git a/src/bitmessageqt/tests/addressbook.py b/src/bitmessageqt/tests/addressbook.py
index cd86c5d6..47d50b96 100644
--- a/src/bitmessageqt/tests/addressbook.py
+++ b/src/bitmessageqt/tests/addressbook.py
@@ -1,7 +1,7 @@
import helper_addressbook
from bitmessageqt.support import createAddressIfNeeded
-from main import TestBase
+from .main import TestBase
class TestAddressbook(TestBase):
diff --git a/src/bitmessageqt/tests/main.py b/src/bitmessageqt/tests/main.py
index d3fda8aa..32e488c0 100644
--- a/src/bitmessageqt/tests/main.py
+++ b/src/bitmessageqt/tests/main.py
@@ -3,7 +3,8 @@
import sys
import unittest
-from PyQt4 import QtCore, QtGui
+from qtpy import QtCore, QtWidgets
+from six import string_types
from six.moves import queue
import bitmessageqt
@@ -20,7 +21,7 @@ class TestBase(unittest.TestCase):
def setUp(self):
self.app = (
- QtGui.QApplication.instance()
+ QtWidgets.QApplication.instance()
or bitmessageqt.BitmessageQtApplication(sys.argv))
self.window = self.app.activeWindow()
if not self.window:
@@ -50,10 +51,7 @@ class TestMain(unittest.TestCase):
def test_translate(self):
"""Check the results of _translate() with various args"""
- self.assertIsInstance(
- _translate("MainWindow", "Test"),
- QtCore.QString
- )
+ self.assertIsInstance(_translate("MainWindow", "Test"), string_types)
class TestUISignaler(TestBase):
diff --git a/src/bitmessageqt/tests/settings.py b/src/bitmessageqt/tests/settings.py
index bad28ed7..9c2e6452 100644
--- a/src/bitmessageqt/tests/settings.py
+++ b/src/bitmessageqt/tests/settings.py
@@ -2,7 +2,7 @@
import threading
import time
-from PyQt4 import QtCore, QtGui, QtTest
+from qtpy import QtCore, QtWidgets, QtTest
from bmconfigparser import config
from bitmessageqt import settings
@@ -48,8 +48,8 @@ class TestSettings(TestBase):
def call_font_dialog():
"""A function to get the open font dialog and accept it"""
- font_dialog = QtGui.QApplication.activeModalWidget()
- self.assertTrue(isinstance(font_dialog, QtGui.QFontDialog))
+ font_dialog = QtWidgets.QApplication.activeModalWidget()
+ self.assertTrue(isinstance(font_dialog, QtWidgets.QFontDialog))
selected_font = font_dialog.currentFont()
self.assertEqual(
config.safeGet('bitmessagesettings', 'font'), '{},{}'.format(
@@ -64,7 +64,7 @@ class TestSettings(TestBase):
def click_font_button():
"""Use QtTest to click the button"""
QtTest.QTest.mouseClick(
- self.dialog.buttonFont, QtCore.Qt.LeftButton)
+ self.dialog.buttonFont, QtCore.Qt.MouseButton.LeftButton)
style_count = style_control.count()
self.assertGreater(style_count, 1)
diff --git a/src/bitmessageqt/tests/support.py b/src/bitmessageqt/tests/support.py
index ba28b73a..d83f90a0 100644
--- a/src/bitmessageqt/tests/support.py
+++ b/src/bitmessageqt/tests/support.py
@@ -4,7 +4,9 @@ import sys
from shared import isAddressInMyAddressBook
-from main import TestBase
+from .main import TestBase
+
+from unqstr import ustr
class TestSupport(TestBase):
@@ -26,8 +28,8 @@ class TestSupport(TestBase):
self.assertEqual(
ui.tabWidget.currentIndex(), ui.tabWidget.indexOf(ui.send))
self.assertEqual(
- ui.lineEditTo.text(), self.SUPPORT_ADDRESS)
+ ustr(ui.lineEditTo.text()), ustr(self.SUPPORT_ADDRESS))
self.assertEqual(
- ui.lineEditSubject.text(), self.SUPPORT_SUBJECT)
+ ustr(ui.lineEditSubject.text()), ustr(self.SUPPORT_SUBJECT))
self.assertIn(
sys.version, ui.textEditMessage.toPlainText())
diff --git a/src/bitmessageqt/uisignaler.py b/src/bitmessageqt/uisignaler.py
index c23ec3bc..8f34aff0 100644
--- a/src/bitmessageqt/uisignaler.py
+++ b/src/bitmessageqt/uisignaler.py
@@ -1,15 +1,35 @@
-
-from PyQt4.QtCore import QThread, SIGNAL
import sys
+from qtpy import QtCore
+
import queues
+from network.node import Peer
-class UISignaler(QThread):
+class UISignaler(QtCore.QThread):
_instance = None
- def __init__(self, parent=None):
- QThread.__init__(self, parent)
+ writeNewAddressToTable = QtCore.Signal(str, str, str)
+ updateStatusBar = QtCore.Signal(object)
+ updateSentItemStatusByToAddress = QtCore.Signal(object, str)
+ updateSentItemStatusByAckdata = QtCore.Signal(object, str)
+ displayNewInboxMessage = QtCore.Signal(object, str, object, object, str)
+ displayNewSentMessage = QtCore.Signal(
+ object, str, str, object, object, str)
+ updateNetworkStatusTab = QtCore.Signal(bool, bool, Peer)
+ updateNumberOfMessagesProcessed = QtCore.Signal()
+ updateNumberOfPubkeysProcessed = QtCore.Signal()
+ updateNumberOfBroadcastsProcessed = QtCore.Signal()
+ setStatusIcon = QtCore.Signal(str)
+ changedInboxUnread = QtCore.Signal(str)
+ rerenderMessagelistFromLabels = QtCore.Signal()
+ rerenderMessagelistToLabels = QtCore.Signal()
+ rerenderAddressBook = QtCore.Signal()
+ rerenderSubscriptions = QtCore.Signal()
+ rerenderBlackWhiteList = QtCore.Signal()
+ removeInboxRowByMsgid = QtCore.Signal(str)
+ newVersionAvailable = QtCore.Signal(str)
+ displayAlert = QtCore.Signal(str, str, bool)
@classmethod
def get(cls):
@@ -22,69 +42,59 @@ class UISignaler(QThread):
command, data = queues.UISignalQueue.get()
if command == 'writeNewAddressToTable':
label, address, streamNumber = data
- self.emit(
- SIGNAL("writeNewAddressToTable(PyQt_PyObject,PyQt_PyObject,PyQt_PyObject)"),
- label,
- address,
- str(streamNumber))
+ self.writeNewAddressToTable.emit(
+ label, address, str(streamNumber))
elif command == 'updateStatusBar':
- self.emit(SIGNAL("updateStatusBar(PyQt_PyObject)"), data)
+ self.updateStatusBar.emit(data)
elif command == 'updateSentItemStatusByToAddress':
toAddress, message = data
- self.emit(SIGNAL(
- "updateSentItemStatusByToAddress(PyQt_PyObject,PyQt_PyObject)"), toAddress, message)
+ self.updateSentItemStatusByToAddress.emit(toAddress, message)
elif command == 'updateSentItemStatusByAckdata':
ackData, message = data
- self.emit(SIGNAL(
- "updateSentItemStatusByAckdata(PyQt_PyObject,PyQt_PyObject)"), ackData, message)
+ self.updateSentItemStatusByAckdata.emit(ackData, message)
elif command == 'displayNewInboxMessage':
inventoryHash, toAddress, fromAddress, subject, body = data
- self.emit(SIGNAL(
- "displayNewInboxMessage(PyQt_PyObject,PyQt_PyObject,PyQt_PyObject,PyQt_PyObject,PyQt_PyObject)"),
- inventoryHash, toAddress, fromAddress, subject, body)
+
+ self.displayNewInboxMessage.emit(
+ inventoryHash, toAddress, fromAddress,
+ subject, body)
elif command == 'displayNewSentMessage':
toAddress, fromLabel, fromAddress, subject, message, ackdata = data
- self.emit(SIGNAL(
- "displayNewSentMessage(PyQt_PyObject,PyQt_PyObject,PyQt_PyObject,PyQt_PyObject,PyQt_PyObject,PyQt_PyObject)"),
- toAddress, fromLabel, fromAddress, subject, message, ackdata)
+ self.displayNewSentMessage.emit(
+ toAddress, fromLabel, fromAddress,
+ subject.decode('utf-8'), message, ackdata)
elif command == 'updateNetworkStatusTab':
outbound, add, destination = data
- self.emit(
- SIGNAL("updateNetworkStatusTab(PyQt_PyObject,PyQt_PyObject,PyQt_PyObject)"),
- outbound,
- add,
- destination)
+ self.updateNetworkStatusTab.emit(outbound, add, destination)
elif command == 'updateNumberOfMessagesProcessed':
- self.emit(SIGNAL("updateNumberOfMessagesProcessed()"))
+ self.updateNumberOfMessagesProcessed.emit()
elif command == 'updateNumberOfPubkeysProcessed':
- self.emit(SIGNAL("updateNumberOfPubkeysProcessed()"))
+ self.updateNumberOfPubkeysProcessed.emit()
elif command == 'updateNumberOfBroadcastsProcessed':
- self.emit(SIGNAL("updateNumberOfBroadcastsProcessed()"))
+ self.updateNumberOfBroadcastsProcessed.emit()
elif command == 'setStatusIcon':
- self.emit(SIGNAL("setStatusIcon(PyQt_PyObject)"), data)
+ self.setStatusIcon.emit(data)
elif command == 'changedInboxUnread':
- self.emit(SIGNAL("changedInboxUnread(PyQt_PyObject)"), data)
+ self.changedInboxUnread.emit(data)
elif command == 'rerenderMessagelistFromLabels':
- self.emit(SIGNAL("rerenderMessagelistFromLabels()"))
+ self.rerenderMessagelistFromLabels.emit()
elif command == 'rerenderMessagelistToLabels':
- self.emit(SIGNAL("rerenderMessagelistToLabels()"))
+ self.rerenderMessagelistToLabels.emit()
elif command == 'rerenderAddressBook':
- self.emit(SIGNAL("rerenderAddressBook()"))
+ self.rerenderAddressBook.emit()
elif command == 'rerenderSubscriptions':
- self.emit(SIGNAL("rerenderSubscriptions()"))
+ self.rerenderSubscriptions.emit()
elif command == 'rerenderBlackWhiteList':
- self.emit(SIGNAL("rerenderBlackWhiteList()"))
+ self.rerenderBlackWhiteList.emit()
elif command == 'removeInboxRowByMsgid':
- self.emit(SIGNAL("removeInboxRowByMsgid(PyQt_PyObject)"), data)
+ self.removeInboxRowByMsgid.emit(data)
elif command == 'newVersionAvailable':
- self.emit(SIGNAL("newVersionAvailable(PyQt_PyObject)"), data)
+ self.newVersionAvailable.emit(data)
elif command == 'alert':
title, text, exitAfterUserClicksOk = data
- self.emit(
- SIGNAL("displayAlert(PyQt_PyObject, PyQt_PyObject, PyQt_PyObject)"),
- title,
- text,
- exitAfterUserClicksOk)
+ self.displayAlert.emit(title, text, exitAfterUserClicksOk)
else:
sys.stderr.write(
- 'Command sent to UISignaler not recognized: %s\n' % command)
+ 'Command sent to UISignaler not recognized: %s\n'
+ % command
+ )
diff --git a/src/bitmessageqt/utils.py b/src/bitmessageqt/utils.py
index 9f849b3b..6484fc4f 100644
--- a/src/bitmessageqt/utils.py
+++ b/src/bitmessageqt/utils.py
@@ -1,7 +1,7 @@
import hashlib
import os
-from PyQt4 import QtGui
+from qtpy import QtGui
import state
from addresses import addBMIfNotPresent
@@ -30,16 +30,18 @@ def identiconize(address):
# It can be used as a pseudo-password to salt the generation of
# the identicons to decrease the risk of attacks where someone creates
# an address to mimic someone else's identicon.
- identiconsuffix = config.get('bitmessagesettings', 'identiconsuffix')
+ data = addBMIfNotPresent(address) + config.get(
+ 'bitmessagesettings', 'identiconsuffix')
+ data = data.encode("utf-8", "replace")
if identicon_lib[:len('qidenticon')] == 'qidenticon':
# originally by:
# :Author:Shin Adachi
# Licesensed under FreeBSD License.
# stripped from PIL and uses QT instead (by sendiulo, same license)
import qidenticon
- icon_hash = hashlib.md5(
- addBMIfNotPresent(address) + identiconsuffix).hexdigest()
- use_two_colors = identicon_lib[:len('qidenticon_two')] == 'qidenticon_two'
+ icon_hash = hashlib.md5(data).hexdigest()
+ use_two_colors = (
+ identicon_lib[:len('qidenticon_two')] == 'qidenticon_two')
opacity = int(
identicon_lib not in (
'qidenticon_x', 'qidenticon_two_x',
@@ -63,8 +65,7 @@ def identiconize(address):
# https://github.com/azaghal/pydenticon
# note that it requires pillow (or PIL) to be installed:
# https://python-pillow.org/
- idcon_render = Pydenticon(
- addBMIfNotPresent(address) + identiconsuffix, size * 3)
+ idcon_render = Pydenticon(data, size * 3)
rendering = idcon_render._render()
data = rendering.convert("RGBA").tostring("raw", "RGBA")
qim = QtGui.QImage(data, size, size, QtGui.QImage.Format_ARGB32)
@@ -81,7 +82,7 @@ def avatarize(address):
falls back to identiconize(address)
"""
idcon = QtGui.QIcon()
- icon_hash = hashlib.md5(addBMIfNotPresent(address)).hexdigest()
+ icon_hash = hashlib.md5(addBMIfNotPresent(address).encode("utf-8", "replace")).hexdigest()
if address == str_broadcast_subscribers:
# don't hash [Broadcast subscribers]
icon_hash = address
@@ -105,11 +106,9 @@ def avatarize(address):
lower_default = state.appdata + 'avatars/' + 'default.' + ext.lower()
upper_default = state.appdata + 'avatars/' + 'default.' + ext.upper()
if os.path.isfile(lower_default):
- default = lower_default
idcon.addFile(lower_default)
return idcon
elif os.path.isfile(upper_default):
- default = upper_default
idcon.addFile(upper_default)
return idcon
# If no avatar is found
diff --git a/src/bitmessageqt/widgets.py b/src/bitmessageqt/widgets.py
index 8ef807f2..e3232fe6 100644
--- a/src/bitmessageqt/widgets.py
+++ b/src/bitmessageqt/widgets.py
@@ -1,13 +1,15 @@
-from PyQt4 import uic
+from qtpy import uic
import os.path
import paths
-import sys
+
def resource_path(resFile):
baseDir = paths.codePath()
- for subDir in ["ui", "bitmessageqt"]:
- if os.path.isdir(os.path.join(baseDir, subDir)) and os.path.isfile(os.path.join(baseDir, subDir, resFile)):
- return os.path.join(baseDir, subDir, resFile)
+ for subDir in ("ui", "bitmessageqt"):
+ path = os.path.join(baseDir, subDir, resFile)
+ if os.path.isfile(path):
+ return path
+
def load(resFile, widget):
uic.loadUi(resource_path(resFile), widget)
diff --git a/src/bmconfigparser.py b/src/bmconfigparser.py
index abf285ad..4d7684db 100644
--- a/src/bmconfigparser.py
+++ b/src/bmconfigparser.py
@@ -9,19 +9,24 @@ from datetime import datetime
from six import string_types
from six.moves import configparser
+from unqstr import ustr
try:
import state
except ImportError:
from pybitmessage import state
-SafeConfigParser = configparser.SafeConfigParser
+try:
+ SafeConfigParser = configparser.SafeConfigParser
+except AttributeError:
+ # alpine linux, python3.12
+ SafeConfigParser = configparser.ConfigParser
config_ready = Event()
class BMConfigParser(SafeConfigParser):
"""
- Singleton class inherited from :class:`ConfigParser.SafeConfigParser`
+ Singleton class inherited from :class:`configparser.SafeConfigParser`
with additional methods specific to bitmessage config.
"""
# pylint: disable=too-many-ancestors
@@ -114,7 +119,8 @@ class BMConfigParser(SafeConfigParser):
"""Return a list of local bitmessage addresses (from section labels)"""
sections = [x for x in self.sections() if x.startswith('BM-')]
if sort:
- sections.sort(key=lambda item: self.get(item, 'label').lower())
+ sections.sort(key=lambda item: ustr(self.get(item, 'label')) \
+ .lower())
return sections
def save(self):
diff --git a/src/class_addressGenerator.py b/src/class_addressGenerator.py
index 33da1371..4927b333 100644
--- a/src/class_addressGenerator.py
+++ b/src/class_addressGenerator.py
@@ -1,5 +1,5 @@
"""
-A thread for creating addresses
+addressGenerator thread class definition
"""
import time
@@ -211,8 +211,8 @@ class addressGenerator(StoppableThread):
'updateStatusBar',
_translate(
"MainWindow",
- "Generating %1 new addresses."
- ).arg(str(numberOfAddressesToMake))
+ "Generating {0} new addresses."
+ ).format(str(numberOfAddressesToMake))
))
signingKeyNonce = 0
encryptionKeyNonce = 1
@@ -302,9 +302,9 @@ class addressGenerator(StoppableThread):
'updateStatusBar',
_translate(
"MainWindow",
- "%1 is already in 'Your Identities'."
+ "{0} is already in 'Your Identities'."
" Not adding it again."
- ).arg(address)
+ ).format(address)
))
else:
self.logger.debug('label: %s', label)
diff --git a/src/class_objectProcessor.py b/src/class_objectProcessor.py
index 974631cb..fc12e22a 100644
--- a/src/class_objectProcessor.py
+++ b/src/class_objectProcessor.py
@@ -12,6 +12,7 @@ import subprocess # nosec B404
import threading
import time
from binascii import hexlify
+import sqlite3
import helper_bitcoin
import helper_inbox
@@ -33,6 +34,7 @@ from helper_sql import (
from network import knownnodes, invQueue
from network.node import Peer
from tr import _translate
+from dbcompat import dbstr
logger = logging.getLogger('default')
@@ -121,7 +123,7 @@ class objectProcessor(threading.Thread):
objectType, data = queues.objectProcessorQueue.get()
sql.execute(
'INSERT INTO objectprocessorqueue VALUES (?,?)',
- objectType, data)
+ objectType, sqlite3.Binary(data))
numberOfObjectsThatWereInTheObjectProcessorQueue += 1
logger.debug(
'Saved %s objects from the objectProcessorQueue to'
@@ -140,19 +142,23 @@ class objectProcessor(threading.Thread):
# bypass nonce and time, retain object type/version/stream + body
readPosition = 16
- if data[readPosition:] in state.ackdataForWhichImWatching:
+ data_bytes = bytes(data[readPosition:])
+ if data_bytes in state.ackdataForWhichImWatching:
logger.info('This object is an acknowledgement bound for me.')
- del state.ackdataForWhichImWatching[data[readPosition:]]
- sqlExecute(
+ del state.ackdataForWhichImWatching[data_bytes]
+ rowcount = sqlExecute(
"UPDATE sent SET status='ackreceived', lastactiontime=?"
- " WHERE ackdata=?", int(time.time()), data[readPosition:])
+ " WHERE ackdata=?", int(time.time()), sqlite3.Binary(data_bytes))
+ if rowcount < 1:
+ rowcount = sqlExecute(
+ "UPDATE sent SET status='ackreceived', lastactiontime=?"
+ " WHERE ackdata=CAST(? AS TEXT)", int(time.time()), data_bytes)
queues.UISignalQueue.put((
'updateSentItemStatusByAckdata', (
- data[readPosition:],
- _translate(
+ data_bytes, _translate(
"MainWindow",
- "Acknowledgement of the message received %1"
- ).arg(l10n.formatTimestamp()))
+ "Acknowledgement of the message received {0}"
+ ).format(l10n.formatTimestamp()))
))
else:
logger.debug('This object is not an acknowledgement bound for me.')
@@ -215,9 +221,10 @@ class objectProcessor(threading.Thread):
logger.info(
'the hash requested in this getpubkey request is: %s',
hexlify(requestedHash))
+ requestedHash_bytes = bytes(requestedHash)
# if this address hash is one of mine
- if requestedHash in shared.myAddressesByHash:
- myAddress = shared.myAddressesByHash[requestedHash]
+ if requestedHash_bytes in shared.myAddressesByHash:
+ myAddress = shared.myAddressesByHash[requestedHash_bytes]
elif requestedAddressVersionNumber >= 4:
requestedTag = data[readPosition:readPosition + 32]
if len(requestedTag) != 32:
@@ -227,8 +234,9 @@ class objectProcessor(threading.Thread):
logger.debug(
'the tag requested in this getpubkey request is: %s',
hexlify(requestedTag))
- if requestedTag in shared.myAddressesByTag:
- myAddress = shared.myAddressesByTag[requestedTag]
+ requestedTag_bytes = bytes(requestedTag)
+ if requestedTag_bytes in shared.myAddressesByTag:
+ myAddress = shared.myAddressesByTag[requestedTag_bytes]
if myAddress == '':
logger.info('This getpubkey request is not for any of my keys.')
@@ -299,12 +307,12 @@ class objectProcessor(threading.Thread):
'(within processpubkey) payloadLength less than 146.'
' Sanity check failed.')
readPosition += 4
- pubSigningKey = '\x04' + data[readPosition:readPosition + 64]
+ pubSigningKey = b'\x04' + data[readPosition:readPosition + 64]
# Is it possible for a public key to be invalid such that trying to
# encrypt or sign with it will cause an error? If it is, it would
# be easiest to test them here.
readPosition += 64
- pubEncryptionKey = '\x04' + data[readPosition:readPosition + 64]
+ pubEncryptionKey = b'\x04' + data[readPosition:readPosition + 64]
if len(pubEncryptionKey) < 65:
return logger.debug(
'publicEncryptionKey length less than 64. Sanity check'
@@ -327,19 +335,19 @@ class objectProcessor(threading.Thread):
queryreturn = sqlQuery(
"SELECT usedpersonally FROM pubkeys WHERE address=?"
- " AND usedpersonally='yes'", address)
+ " AND usedpersonally='yes'", dbstr(address))
# if this pubkey is already in our database and if we have
# used it personally:
if queryreturn != []:
logger.info(
'We HAVE used this pubkey personally. Updating time.')
- t = (address, addressVersion, dataToStore,
+ t = (dbstr(address), addressVersion, sqlite3.Binary(dataToStore),
int(time.time()), 'yes')
else:
logger.info(
'We have NOT used this pubkey personally. Inserting'
' in database.')
- t = (address, addressVersion, dataToStore,
+ t = (dbstr(address), addressVersion, sqlite3.Binary(dataToStore),
int(time.time()), 'no')
sqlExecute('''INSERT INTO pubkeys VALUES (?,?,?,?,?)''', *t)
self.possibleNewPubkey(address)
@@ -350,9 +358,9 @@ class objectProcessor(threading.Thread):
' Sanity check failed.')
return
readPosition += 4
- pubSigningKey = '\x04' + data[readPosition:readPosition + 64]
+ pubSigningKey = b'\x04' + data[readPosition:readPosition + 64]
readPosition += 64
- pubEncryptionKey = '\x04' + data[readPosition:readPosition + 64]
+ pubEncryptionKey = b'\x04' + data[readPosition:readPosition + 64]
readPosition += 64
specifiedNonceTrialsPerByteLength = decodeVarint(
data[readPosition:readPosition + 10])[1]
@@ -389,20 +397,20 @@ class objectProcessor(threading.Thread):
address = encodeAddress(addressVersion, streamNumber, ripe)
queryreturn = sqlQuery(
"SELECT usedpersonally FROM pubkeys WHERE address=?"
- " AND usedpersonally='yes'", address)
+ " AND usedpersonally='yes'", dbstr(address))
# if this pubkey is already in our database and if we have
# used it personally:
if queryreturn != []:
logger.info(
'We HAVE used this pubkey personally. Updating time.')
- t = (address, addressVersion, dataToStore,
- int(time.time()), 'yes')
+ t = (dbstr(address), addressVersion, sqlite3.Binary(dataToStore),
+ int(time.time()), dbstr('yes'))
else:
logger.info(
'We have NOT used this pubkey personally. Inserting'
' in database.')
- t = (address, addressVersion, dataToStore,
- int(time.time()), 'no')
+ t = (dbstr(address), addressVersion, sqlite3.Binary(dataToStore),
+ int(time.time()), dbstr('no'))
sqlExecute('''INSERT INTO pubkeys VALUES (?,?,?,?,?)''', *t)
self.possibleNewPubkey(address)
@@ -413,12 +421,13 @@ class objectProcessor(threading.Thread):
' Sanity check failed.')
tag = data[readPosition:readPosition + 32]
- if tag not in state.neededPubkeys:
+ tag_bytes = bytes(tag)
+ if tag_bytes not in state.neededPubkeys:
return logger.info(
'We don\'t need this v4 pubkey. We didn\'t ask for it.')
# Let us try to decrypt the pubkey
- toAddress = state.neededPubkeys[tag][0]
+ toAddress = state.neededPubkeys[tag_bytes][0]
if protocol.decryptAndCheckPubkeyPayload(data, toAddress) == \
'successful':
# At this point we know that we have been waiting on this
@@ -483,7 +492,7 @@ class objectProcessor(threading.Thread):
# This is a message bound for me.
# Look up my address based on the RIPE hash.
- toAddress = shared.myAddressesByHash[toRipe]
+ toAddress = shared.myAddressesByHash[bytes(toRipe)]
readPosition = 0
sendersAddressVersionNumber, sendersAddressVersionNumberLength = \
decodeVarint(decryptedData[readPosition:readPosition + 10])
@@ -507,9 +516,9 @@ class objectProcessor(threading.Thread):
return
readPosition += sendersStreamNumberLength
readPosition += 4
- pubSigningKey = '\x04' + decryptedData[readPosition:readPosition + 64]
+ pubSigningKey = b'\x04' + decryptedData[readPosition:readPosition + 64]
readPosition += 64
- pubEncryptionKey = '\x04' + decryptedData[readPosition:readPosition + 64]
+ pubEncryptionKey = b'\x04' + decryptedData[readPosition:readPosition + 64]
readPosition += 64
if sendersAddressVersionNumber >= 3:
requiredAverageProofOfWorkNonceTrialsPerByte, varintLength = \
@@ -558,7 +567,7 @@ class objectProcessor(threading.Thread):
readPosition += signatureLengthLength
signature = decryptedData[
readPosition:readPosition + signatureLength]
- signedData = data[8:20] + encodeVarint(1) + encodeVarint(
+ signedData = bytes(data[8:20]) + encodeVarint(1) + encodeVarint(
streamNumberAsClaimedByMsg
) + decryptedData[:positionOfBottomOfAckData]
@@ -590,11 +599,11 @@ class objectProcessor(threading.Thread):
# person.
sqlExecute(
'''INSERT INTO pubkeys VALUES (?,?,?,?,?)''',
- fromAddress,
+ dbstr(fromAddress),
sendersAddressVersionNumber,
- decryptedData[:endOfThePublicKeyPosition],
+ sqlite3.Binary(decryptedData[:endOfThePublicKeyPosition]),
int(time.time()),
- 'yes')
+ dbstr('yes'))
# Check to see whether we happen to be awaiting this
# pubkey in order to send a message. If we are, it will do the POW
@@ -631,7 +640,7 @@ class objectProcessor(threading.Thread):
'bitmessagesettings', 'blackwhitelist') == 'black':
queryreturn = sqlQuery(
"SELECT label FROM blacklist where address=? and enabled='1'",
- fromAddress)
+ dbstr(fromAddress))
if queryreturn != []:
logger.info('Message ignored because address is in blacklist.')
@@ -639,7 +648,7 @@ class objectProcessor(threading.Thread):
else: # We're using a whitelist
queryreturn = sqlQuery(
"SELECT label FROM whitelist where address=? and enabled='1'",
- fromAddress)
+ dbstr(fromAddress))
if queryreturn == []:
logger.info(
'Message ignored because address not in whitelist.')
@@ -808,13 +817,14 @@ class objectProcessor(threading.Thread):
elif broadcastVersion == 5:
embeddedTag = data[readPosition:readPosition + 32]
readPosition += 32
- if embeddedTag not in shared.MyECSubscriptionCryptorObjects:
+ embeddedTag_bytes = bytes(embeddedTag)
+ if embeddedTag_bytes not in shared.MyECSubscriptionCryptorObjects:
logger.debug('We\'re not interested in this broadcast.')
return
# We are interested in this broadcast because of its tag.
# We're going to add some more data which is signed further down.
- signedData = data[8:readPosition]
- cryptorObject = shared.MyECSubscriptionCryptorObjects[embeddedTag]
+ signedData = bytes(data[8:readPosition])
+ cryptorObject = shared.MyECSubscriptionCryptorObjects[embeddedTag_bytes]
try:
decryptedData = cryptorObject.decrypt(data[readPosition:])
logger.debug('EC decryption successful')
@@ -854,10 +864,10 @@ class objectProcessor(threading.Thread):
)
readPosition += sendersStreamLength
readPosition += 4
- sendersPubSigningKey = '\x04' + \
+ sendersPubSigningKey = b'\x04' + \
decryptedData[readPosition:readPosition + 64]
readPosition += 64
- sendersPubEncryptionKey = '\x04' + \
+ sendersPubEncryptionKey = b'\x04' + \
decryptedData[readPosition:readPosition + 64]
readPosition += 64
if sendersAddressVersion >= 3:
@@ -927,11 +937,11 @@ class objectProcessor(threading.Thread):
# Let's store the public key in case we want to reply to this person.
sqlExecute('''INSERT INTO pubkeys VALUES (?,?,?,?,?)''',
- fromAddress,
- sendersAddressVersion,
- decryptedData[:endOfPubkeyPosition],
+ dbstr(fromAddress),
+ dbstr(sendersAddressVersion),
+ sqlite3.Binary(decryptedData[:endOfPubkeyPosition]),
int(time.time()),
- 'yes')
+ dbstr('yes'))
# Check to see whether we happen to be awaiting this
# pubkey in order to send a message. If we are, it will do the POW
@@ -997,8 +1007,9 @@ class objectProcessor(threading.Thread):
encodeVarint(addressVersion) + encodeVarint(streamNumber)
+ ripe
)[32:]
- if tag in state.neededPubkeys:
- del state.neededPubkeys[tag]
+ tag_bytes = bytes(tag)
+ if tag_bytes in state.neededPubkeys:
+ del state.neededPubkeys[tag_bytes]
self.sendMessages(address)
@staticmethod
@@ -1012,7 +1023,7 @@ class objectProcessor(threading.Thread):
"UPDATE sent SET status='doingmsgpow', retrynumber=0"
" WHERE toaddress=?"
" AND (status='awaitingpubkey' OR status='doingpubkeypow')"
- " AND folder='sent'", address)
+ " AND folder='sent'", dbstr(address))
queues.workerQueue.put(('sendmessage', ''))
@staticmethod
@@ -1047,8 +1058,8 @@ class objectProcessor(threading.Thread):
if checksum != hashlib.sha512(payload).digest()[0:4]:
logger.info('ackdata checksum wrong. Not sending ackdata.')
return False
- command = command.rstrip('\x00')
- if command != 'object':
+ command = command.rstrip(b'\x00')
+ if command != b'object':
return False
return True
diff --git a/src/class_singleCleaner.py b/src/class_singleCleaner.py
index 06153dcf..0c6ec021 100644
--- a/src/class_singleCleaner.py
+++ b/src/class_singleCleaner.py
@@ -22,6 +22,7 @@ It resends messages when there has been no response:
import gc
import os
import time
+import sqlite3
import queues
import state
@@ -29,6 +30,7 @@ from bmconfigparser import config
from helper_sql import sqlExecute, sqlQuery
from network import connectionpool, knownnodes, StoppableThread
from tr import _translate
+from dbcompat import dbstr
#: Equals 4 weeks. You could make this longer if you want
@@ -99,6 +101,8 @@ class singleCleaner(StoppableThread):
tick - state.maximumLengthOfTimeToBotherResendingMessages
)
for toAddress, ackData, status in queryreturn:
+ toAddress = toAddress.decode("utf-8", "replace")
+ status = status.decode("utf-8", "replace")
if status == 'awaitingpubkey':
self.resendPubkeyRequest(toAddress)
elif status == 'msgsent':
@@ -168,7 +172,7 @@ class singleCleaner(StoppableThread):
))
sqlExecute(
"UPDATE sent SET status = 'msgqueued'"
- " WHERE toaddress = ? AND folder = 'sent'", address)
+ " WHERE toaddress = ? AND folder = 'sent'", dbstr(address))
queues.workerQueue.put(('sendmessage', ''))
def resendMsg(self, ackdata):
@@ -177,9 +181,13 @@ class singleCleaner(StoppableThread):
'It has been a long time and we haven\'t heard an acknowledgement'
' to our msg. Sending again.'
)
- sqlExecute(
+ rowcount = sqlExecute(
"UPDATE sent SET status = 'msgqueued'"
- " WHERE ackdata = ? AND folder = 'sent'", ackdata)
+ " WHERE ackdata = ? AND folder = 'sent'", sqlite3.Binary(ackdata))
+ if rowcount < 1:
+ sqlExecute(
+ "UPDATE sent SET status = 'msgqueued'"
+ " WHERE ackdata = CAST(? AS TEXT) AND folder = 'sent'", ackdata)
queues.workerQueue.put(('sendmessage', ''))
queues.UISignalQueue.put((
'updateStatusBar',
diff --git a/src/class_singleWorker.py b/src/class_singleWorker.py
index f79d9240..d3486c26 100644
--- a/src/class_singleWorker.py
+++ b/src/class_singleWorker.py
@@ -11,6 +11,7 @@ import time
from binascii import hexlify, unhexlify
from struct import pack
from subprocess import call # nosec
+import sqlite3
import defaults
import helper_inbox
@@ -24,12 +25,15 @@ import protocol
import queues
import shared
import state
-import tr
from addresses import decodeAddress, decodeVarint, encodeVarint
from bmconfigparser import config
from helper_sql import sqlExecute, sqlQuery
from network import knownnodes, StoppableThread, invQueue
from six.moves import configparser, queue
+from six.moves.reprlib import repr
+import six
+from dbcompat import dbstr
+from tr import _translate
def sizeof_fmt(num, suffix='h/s'):
@@ -73,6 +77,7 @@ class singleWorker(StoppableThread):
'''SELECT DISTINCT toaddress FROM sent'''
''' WHERE (status='awaitingpubkey' AND folder='sent')''')
for toAddress, in queryreturn:
+ toAddress = toAddress.decode("utf-8", "replace")
toAddressVersionNumber, toStreamNumber, toRipe = \
decodeAddress(toAddress)[1:]
if toAddressVersionNumber <= 3:
@@ -87,7 +92,7 @@ class singleWorker(StoppableThread):
tag = doubleHashOfAddressData[32:]
# We'll need this for when we receive a pubkey reply:
# it will be encrypted and we'll need to decrypt it.
- state.neededPubkeys[tag] = (
+ state.neededPubkeys[bytes(tag)] = (
toAddress,
highlevelcrypto.makeCryptor(
hexlify(privEncryptionKey))
@@ -99,18 +104,23 @@ class singleWorker(StoppableThread):
for row in queryreturn:
ackdata, = row
self.logger.info('Watching for ackdata %s', hexlify(ackdata))
- state.ackdataForWhichImWatching[ackdata] = 0
+ state.ackdataForWhichImWatching[bytes(ackdata)] = 0
# Fix legacy (headerless) watched ackdata to include header
for oldack in state.ackdataForWhichImWatching:
if len(oldack) == 32:
# attach legacy header, always constant (msg/1/1)
- newack = '\x00\x00\x00\x02\x01\x01' + oldack
- state.ackdataForWhichImWatching[newack] = 0
- sqlExecute(
+ newack = b'\x00\x00\x00\x02\x01\x01' + oldack
+ state.ackdataForWhichImWatching[bytes(newack)] = 0
+ rowcount = sqlExecute(
'''UPDATE sent SET ackdata=? WHERE ackdata=? AND folder = 'sent' ''',
- newack, oldack
+ sqlite3.Binary(newack), sqlite3.Binary(oldack)
)
+ if rowcount < 1:
+ sqlExecute(
+ '''UPDATE sent SET ackdata=? WHERE ackdata=CAST(? AS TEXT) AND folder = 'sent' ''',
+ sqlite3.Binary(newack), oldack
+ )
del state.ackdataForWhichImWatching[oldack]
# For the case if user deleted knownnodes
@@ -217,9 +227,8 @@ class singleWorker(StoppableThread):
return privSigningKeyHex, privEncryptionKeyHex, \
pubSigningKey, pubEncryptionKey
- def _doPOWDefaults(self, payload, TTL,
- log_prefix='',
- log_time=False):
+ def _doPOWDefaults(
+ self, payload, TTL, log_prefix='', log_time=False):
target = 2 ** 64 / (
defaults.networkDefaultProofOfWorkNonceTrialsPerByte * (
len(payload) + 8
@@ -245,14 +254,16 @@ class singleWorker(StoppableThread):
'PoW took %.1f seconds, speed %s.',
delta, sizeof_fmt(nonce / delta)
)
- except: # noqa:E722 # NameError
+ except NameError:
self.logger.warning("Proof of Work exception")
payload = pack('>Q', nonce) + payload
return payload
def doPOWForMyV2Pubkey(self, adressHash):
- """ This function also broadcasts out the pubkey
- message once it is done with the POW"""
+ """
+ This function also broadcasts out the pubkey message once it is
+ done with the POW
+ """
# Look up my stream number based on my address hash
myAddress = shared.myAddressesByHash[adressHash]
addressVersionNumber, streamNumber = decodeAddress(myAddress)[1:3]
@@ -261,7 +272,7 @@ class singleWorker(StoppableThread):
TTL = int(28 * 24 * 60 * 60 + helper_random.randomrandrange(-300, 300))
embeddedTime = int(time.time() + TTL)
payload = pack('>Q', (embeddedTime))
- payload += '\x00\x00\x00\x01' # object type: pubkey
+ payload += b'\x00\x00\x00\x01' # object type: pubkey
payload += encodeVarint(addressVersionNumber) # Address version number
payload += encodeVarint(streamNumber)
# bitfield of features supported by me (see the wiki).
@@ -308,9 +319,10 @@ class singleWorker(StoppableThread):
def sendOutOrStoreMyV3Pubkey(self, adressHash):
"""
- If this isn't a chan address, this function assembles the pubkey data, does the necessary POW and sends it out.
- If it *is* a chan then it assembles the pubkey and stores is in the pubkey table so that we can send messages
- to "ourselves".
+ If this isn't a chan address, this function assembles the pubkey
+ data, does the necessary POW and sends it out.
+ If it *is* a chan then it assembles the pubkey and stores it in
+ the pubkey table so that we can send messages to "ourselves".
"""
try:
myAddress = shared.myAddressesByHash[adressHash]
@@ -338,7 +350,7 @@ class singleWorker(StoppableThread):
# expiresTime time.
payload = pack('>Q', (embeddedTime))
- payload += '\x00\x00\x00\x01' # object type: pubkey
+ payload += b'\x00\x00\x00\x01' # object type: pubkey
payload += encodeVarint(addressVersionNumber) # Address version number
payload += encodeVarint(streamNumber)
# bitfield of features supported by me (see the wiki).
@@ -396,9 +408,10 @@ class singleWorker(StoppableThread):
def sendOutOrStoreMyV4Pubkey(self, myAddress):
"""
- It doesn't send directly anymore. It put is to a queue for another thread to send at an appropriate time,
- whereas in the past it directly appended it to the outgoing buffer, I think. Same with all the other methods in
- this class.
+ It doesn't send directly anymore. It put is to a queue for
+ another thread to send at an appropriate time, whereas in the
+ past it directly appended it to the outgoing buffer, I think.
+ Same with all the other methods in this class.
"""
if not config.has_section(myAddress):
# The address has been deleted.
@@ -413,7 +426,7 @@ class singleWorker(StoppableThread):
TTL = int(28 * 24 * 60 * 60 + helper_random.randomrandrange(-300, 300))
embeddedTime = int(time.time() + TTL)
payload = pack('>Q', (embeddedTime))
- payload += '\x00\x00\x00\x01' # object type: pubkey
+ payload += b'\x00\x00\x00\x01' # object type: pubkey
payload += encodeVarint(addressVersionNumber) # Address version number
payload += encodeVarint(streamNumber)
dataToEncrypt = protocol.getBitfield(myAddress)
@@ -515,9 +528,15 @@ class singleWorker(StoppableThread):
payload, TTL, log_prefix='(For onionpeer object)')
inventoryHash = highlevelcrypto.calculateInventoryHash(payload)
+ if six.PY2:
+ payload_buffer = buffer(payload)
+ tag_buffer = buffer(tag)
+ else: # assume six.PY3
+ payload_buffer = memoryview(payload)
+ tag_buffer = memoryview(tag)
state.Inventory[inventoryHash] = (
- objectType, streamNumber, buffer(payload), # noqa: F821
- embeddedTime, buffer(tag) # noqa: F821
+ objectType, streamNumber, payload_buffer, # noqa: F821
+ embeddedTime, tag_buffer # noqa: F821
)
self.logger.info(
'sending inv (within sendOnionPeerObj function) for object: %s',
@@ -525,7 +544,10 @@ class singleWorker(StoppableThread):
invQueue.put((streamNumber, inventoryHash))
def sendBroadcast(self):
- """Send a broadcast-type object (assemble the object, perform PoW and put it to the inv announcement queue)"""
+ """
+ Send a broadcast-type object (assemble the object, perform PoW
+ and put it to the inv announcement queue)
+ """
# Reset just in case
sqlExecute(
'''UPDATE sent SET status='broadcastqueued' '''
@@ -534,10 +556,13 @@ class singleWorker(StoppableThread):
queryreturn = sqlQuery(
'''SELECT fromaddress, subject, message, '''
''' ackdata, ttl, encodingtype FROM sent '''
- ''' WHERE status=? and folder='sent' ''', 'broadcastqueued')
+ ''' WHERE status=? and folder='sent' ''', dbstr('broadcastqueued'))
for row in queryreturn:
fromaddress, subject, body, ackdata, TTL, encoding = row
+ fromaddress = fromaddress.decode("utf-8", "replace")
+ subject = subject.decode("utf-8", "replace")
+ body = body.decode("utf-8", "replace")
# status
_, addressVersionNumber, streamNumber, ripe = \
decodeAddress(fromaddress)
@@ -556,8 +581,7 @@ class singleWorker(StoppableThread):
except ValueError:
queues.UISignalQueue.put((
'updateSentItemStatusByAckdata', (
- ackdata,
- tr._translate(
+ ackdata, _translate(
"MainWindow",
"Error! Could not find sender address"
" (your address) in the keys.dat file."))
@@ -572,17 +596,24 @@ class singleWorker(StoppableThread):
queues.UISignalQueue.put((
'updateSentItemStatusByAckdata', (
ackdata,
- tr._translate(
+ _translate(
"MainWindow",
"Error, can't send."))
))
continue
- if not sqlExecute(
+ rowcount = sqlExecute(
'''UPDATE sent SET status='doingbroadcastpow' '''
''' WHERE ackdata=? AND status='broadcastqueued' '''
''' AND folder='sent' ''',
- ackdata):
+ sqlite3.Binary(ackdata))
+ if rowcount < 1:
+ rowcount = sqlExecute(
+ '''UPDATE sent SET status='doingbroadcastpow' '''
+ ''' WHERE ackdata=CAST(? AS TEXT) AND status='broadcastqueued' '''
+ ''' AND folder='sent' ''',
+ ackdata)
+ if rowcount < 1:
continue
# At this time these pubkeys are 65 bytes long
@@ -599,7 +630,7 @@ class singleWorker(StoppableThread):
TTL = int(TTL + helper_random.randomrandrange(-300, 300))
embeddedTime = int(time.time() + TTL)
payload = pack('>Q', embeddedTime)
- payload += '\x00\x00\x00\x03' # object type: broadcast
+ payload += b'\x00\x00\x00\x03' # object type: broadcast
if addressVersionNumber <= 3:
payload += encodeVarint(4) # broadcast version
@@ -615,7 +646,7 @@ class singleWorker(StoppableThread):
tag = doubleHashOfAddressData[32:]
payload += tag
else:
- tag = ''
+ tag = b''
dataToEncrypt = encodeVarint(addressVersionNumber)
dataToEncrypt += encodeVarint(streamNumber)
@@ -661,8 +692,7 @@ class singleWorker(StoppableThread):
queues.UISignalQueue.put((
'updateSentItemStatusByAckdata', (
- ackdata,
- tr._translate(
+ ackdata, _translate(
"MainWindow",
"Doing work necessary to send broadcast..."))
))
@@ -694,23 +724,30 @@ class singleWorker(StoppableThread):
queues.UISignalQueue.put((
'updateSentItemStatusByAckdata', (
- ackdata,
- tr._translate(
- "MainWindow",
- "Broadcast sent on %1"
- ).arg(l10n.formatTimestamp()))
+ ackdata, _translate(
+ "MainWindow", "Broadcast sent on {0}"
+ ).format(l10n.formatTimestamp()))
))
# Update the status of the message in the 'sent' table to have
# a 'broadcastsent' status
- sqlExecute(
+ rowcount = sqlExecute(
'''UPDATE sent SET msgid=?, status=?, lastactiontime=? '''
''' WHERE ackdata=? AND folder='sent' ''',
- inventoryHash, 'broadcastsent', int(time.time()), ackdata
+ sqlite3.Binary(inventoryHash), dbstr('broadcastsent'), int(time.time()), sqlite3.Binary(ackdata)
)
+ if rowcount < 1:
+ sqlExecute(
+ '''UPDATE sent SET msgid=?, status=?, lastactiontime=? '''
+ ''' WHERE ackdata=CAST(? AS TEXT) AND folder='sent' ''',
+ sqlite3.Binary(inventoryHash), 'broadcastsent', int(time.time()), ackdata
+ )
def sendMsg(self):
- """Send a message-type object (assemble the object, perform PoW and put it to the inv announcement queue)"""
+ """
+ Send a message-type object (assemble the object, perform PoW
+ and put it to the inv announcement queue)
+ """
# pylint: disable=too-many-nested-blocks
# Reset just in case
sqlExecute(
@@ -726,6 +763,11 @@ class singleWorker(StoppableThread):
for row in queryreturn:
toaddress, fromaddress, subject, message, \
ackdata, status, TTL, retryNumber, encoding = row
+ toaddress = toaddress.decode("utf-8", "replace")
+ fromaddress = fromaddress.decode("utf-8", "replace")
+ subject = subject.decode("utf-8", "replace")
+ message = message.decode("utf-8", "replace")
+ status = status.decode("utf-8", "replace")
# toStatus
_, toAddressVersionNumber, toStreamNumber, toRipe = \
decodeAddress(toaddress)
@@ -755,7 +797,7 @@ class singleWorker(StoppableThread):
if not sqlExecute(
'''UPDATE sent SET status='doingmsgpow' '''
''' WHERE toaddress=? AND status='msgqueued' AND folder='sent' ''',
- toaddress
+ dbstr(toaddress)
):
continue
status = 'doingmsgpow'
@@ -763,7 +805,7 @@ class singleWorker(StoppableThread):
# Let's see if we already have the pubkey in our pubkeys table
queryreturn = sqlQuery(
'''SELECT address FROM pubkeys WHERE address=?''',
- toaddress
+ dbstr(toaddress)
)
# If we have the needed pubkey in the pubkey table already,
if queryreturn != []:
@@ -771,7 +813,7 @@ class singleWorker(StoppableThread):
if not sqlExecute(
'''UPDATE sent SET status='doingmsgpow' '''
''' WHERE toaddress=? AND status='msgqueued' AND folder='sent' ''',
- toaddress
+ dbstr(toaddress)
):
continue
status = 'doingmsgpow'
@@ -783,31 +825,31 @@ class singleWorker(StoppableThread):
sqlExecute(
'''UPDATE pubkeys SET usedpersonally='yes' '''
''' WHERE address=?''',
- toaddress
+ dbstr(toaddress)
)
# We don't have the needed pubkey in the pubkeys table already.
else:
if toAddressVersionNumber <= 3:
- toTag = ''
+ toTag = b''
else:
toTag = highlevelcrypto.double_sha512(
encodeVarint(toAddressVersionNumber)
+ encodeVarint(toStreamNumber) + toRipe
)[32:]
+ toTag_bytes = bytes(toTag)
if toaddress in state.neededPubkeys or \
- toTag in state.neededPubkeys:
+ toTag_bytes in state.neededPubkeys:
# We already sent a request for the pubkey
sqlExecute(
'''UPDATE sent SET status='awaitingpubkey', '''
''' sleeptill=? WHERE toaddress=? '''
''' AND status='msgqueued' ''',
int(time.time()) + 2.5 * 24 * 60 * 60,
- toaddress
+ dbstr(toaddress)
)
queues.UISignalQueue.put((
'updateSentItemStatusByToAddress', (
- toaddress,
- tr._translate(
+ toaddress, _translate(
"MainWindow",
"Encryption key was requested earlier."))
))
@@ -836,7 +878,8 @@ class singleWorker(StoppableThread):
privEncryptionKey = doubleHashOfToAddressData[:32]
# The second half of the sha512 hash.
tag = doubleHashOfToAddressData[32:]
- state.neededPubkeys[tag] = (
+ tag_bytes = bytes(tag)
+ state.neededPubkeys[tag_bytes] = (
toaddress,
highlevelcrypto.makeCryptor(
hexlify(privEncryptionKey))
@@ -858,8 +901,8 @@ class singleWorker(StoppableThread):
''' status='awaitingpubkey' or '''
''' status='doingpubkeypow') AND '''
''' folder='sent' ''',
- toaddress)
- del state.neededPubkeys[tag]
+ dbstr(toaddress))
+ del state.neededPubkeys[tag_bytes]
break
# else:
# There was something wrong with this
@@ -875,12 +918,11 @@ class singleWorker(StoppableThread):
'''UPDATE sent SET '''
''' status='doingpubkeypow' WHERE '''
''' toaddress=? AND status='msgqueued' AND folder='sent' ''',
- toaddress
+ dbstr(toaddress)
)
queues.UISignalQueue.put((
'updateSentItemStatusByToAddress', (
- toaddress,
- tr._translate(
+ toaddress, _translate(
"MainWindow",
"Sending a request for the"
" recipient\'s encryption key."))
@@ -901,11 +943,10 @@ class singleWorker(StoppableThread):
# if we aren't sending this to ourselves or a chan
if not config.has_section(toaddress):
- state.ackdataForWhichImWatching[ackdata] = 0
+ state.ackdataForWhichImWatching[bytes(ackdata)] = 0
queues.UISignalQueue.put((
'updateSentItemStatusByAckdata', (
- ackdata,
- tr._translate(
+ ackdata, _translate(
"MainWindow",
"Looking up the receiver\'s public key"))
))
@@ -920,7 +961,7 @@ class singleWorker(StoppableThread):
# is too hard then we'll abort.
queryreturn = sqlQuery(
'SELECT transmitdata FROM pubkeys WHERE address=?',
- toaddress)
+ dbstr(toaddress))
for row in queryreturn: # pylint: disable=redefined-outer-name
pubkeyPayload, = row
@@ -962,15 +1003,14 @@ class singleWorker(StoppableThread):
)
queues.UISignalQueue.put((
'updateSentItemStatusByAckdata', (
- ackdata,
- tr._translate(
+ ackdata, _translate(
"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()))
+ " your settings. {0}"
+ ).format(l10n.formatTimestamp()))
))
# if the human changes their setting and then
# sends another message or restarts their client,
@@ -993,8 +1033,7 @@ class singleWorker(StoppableThread):
defaults.networkDefaultPayloadLengthExtraBytes
queues.UISignalQueue.put((
'updateSentItemStatusByAckdata', (
- ackdata,
- tr._translate(
+ ackdata, _translate(
"MainWindow",
"Doing work necessary to send message.\n"
"There is no required difficulty for"
@@ -1026,32 +1065,19 @@ class singleWorker(StoppableThread):
requiredAverageProofOfWorkNonceTrialsPerByte,
requiredPayloadLengthExtraBytes
)
-
- queues.UISignalQueue.put(
- (
- 'updateSentItemStatusByAckdata',
- (
- ackdata,
- tr._translate(
- "MainWindow",
- "Doing work necessary to send message.\n"
- "Receiver\'s required difficulty: %1"
- " and %2"
- ).arg(
- str(
- float(requiredAverageProofOfWorkNonceTrialsPerByte)
- / defaults.networkDefaultProofOfWorkNonceTrialsPerByte
- )
- ).arg(
- str(
- float(requiredPayloadLengthExtraBytes)
- / defaults.networkDefaultPayloadLengthExtraBytes
- )
- )
- )
- )
- )
-
+ queues.UISignalQueue.put((
+ 'updateSentItemStatusByAckdata', (
+ ackdata, _translate(
+ "MainWindow",
+ "Doing work necessary to send message.\n"
+ "Receiver\'s required difficulty: {0} and {1}"
+ ).format(
+ float(requiredAverageProofOfWorkNonceTrialsPerByte)
+ / defaults.networkDefaultProofOfWorkNonceTrialsPerByte,
+ float(requiredPayloadLengthExtraBytes)
+ / defaults.networkDefaultPayloadLengthExtraBytes
+ ))
+ ))
if status != 'forcepow':
maxacceptablenoncetrialsperbyte = config.getint(
'bitmessagesettings', 'maxacceptablenoncetrialsperbyte')
@@ -1065,24 +1091,30 @@ class singleWorker(StoppableThread):
if cond1 or cond2:
# The demanded difficulty is more than
# we are willing to do.
- sqlExecute(
+ rowcount = sqlExecute(
'''UPDATE sent SET status='toodifficult' '''
''' WHERE ackdata=? AND folder='sent' ''',
- ackdata)
+ sqlite3.Binary(ackdata))
+ if rowcount < 1:
+ sqlExecute(
+ '''UPDATE sent SET status='toodifficult' '''
+ ''' WHERE ackdata=CAST(? AS TEXT) AND folder='sent' ''',
+ ackdata)
queues.UISignalQueue.put((
'updateSentItemStatusByAckdata', (
- ackdata,
- tr._translate(
+ ackdata, _translate(
"MainWindow",
- "Problem: The work demanded by"
- " the recipient (%1 and %2) is"
- " more difficult than you are"
- " willing to do. %3"
- ).arg(str(float(requiredAverageProofOfWorkNonceTrialsPerByte)
- / defaults.networkDefaultProofOfWorkNonceTrialsPerByte)
- ).arg(str(float(requiredPayloadLengthExtraBytes)
- / defaults.networkDefaultPayloadLengthExtraBytes)
- ).arg(l10n.formatTimestamp()))))
+ "Problem: The work demanded by the"
+ " recipient ({0} and {1}) is more"
+ " difficult than you are willing"
+ " to do. {2}"
+ ).format(
+ float(requiredAverageProofOfWorkNonceTrialsPerByte)
+ / defaults.networkDefaultProofOfWorkNonceTrialsPerByte,
+ float(requiredPayloadLengthExtraBytes)
+ / defaults.networkDefaultPayloadLengthExtraBytes,
+ l10n.formatTimestamp()))
+ ))
continue
else: # if we are sending a message to ourselves or a chan..
self.logger.info('Sending a message.')
@@ -1096,15 +1128,14 @@ class singleWorker(StoppableThread):
except (configparser.NoSectionError, configparser.NoOptionError) as err:
queues.UISignalQueue.put((
'updateSentItemStatusByAckdata', (
- ackdata,
- tr._translate(
+ ackdata, _translate(
"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()))
+ " message. {0}"
+ ).format(l10n.formatTimestamp()))
))
self.logger.error(
'Error within sendMsg. Could not read the keys'
@@ -1122,8 +1153,7 @@ class singleWorker(StoppableThread):
defaults.networkDefaultPayloadLengthExtraBytes
queues.UISignalQueue.put((
'updateSentItemStatusByAckdata', (
- ackdata,
- tr._translate(
+ ackdata, _translate(
"MainWindow",
"Doing work necessary to send message."))
))
@@ -1145,8 +1175,7 @@ class singleWorker(StoppableThread):
except ValueError:
queues.UISignalQueue.put((
'updateSentItemStatusByAckdata', (
- ackdata,
- tr._translate(
+ ackdata, _translate(
"MainWindow",
"Error! Could not find sender address"
" (your address) in the keys.dat file."))
@@ -1161,7 +1190,7 @@ class singleWorker(StoppableThread):
queues.UISignalQueue.put((
'updateSentItemStatusByAckdata', (
ackdata,
- tr._translate(
+ _translate(
"MainWindow",
"Error, can't send."))
))
@@ -1201,23 +1230,22 @@ class singleWorker(StoppableThread):
'Not bothering to include ackdata because we are'
' sending to ourselves or a chan.'
)
- fullAckPayload = ''
+ fullAckPayload = b''
elif not protocol.checkBitfield(
behaviorBitfield, protocol.BITFIELD_DOESACK):
self.logger.info(
'Not bothering to include ackdata because'
' the receiver said that they won\'t relay it anyway.'
)
- fullAckPayload = ''
+ fullAckPayload = b''
else:
# The fullAckPayload is a normal msg protocol message
# with the proof of work already completed that the
# receiver of this message can easily send out.
- fullAckPayload = self.generateFullAckMessage(
- ackdata, toStreamNumber, TTL)
+ fullAckPayload = self.generateFullAckMessage(ackdata, TTL)
payload += encodeVarint(len(fullAckPayload))
payload += fullAckPayload
- dataToSign = pack('>Q', embeddedTime) + '\x00\x00\x00\x02' + \
+ dataToSign = pack('>Q', embeddedTime) + b'\x00\x00\x00\x02' + \
encodeVarint(1) + encodeVarint(toStreamNumber) + payload
signature = highlevelcrypto.sign(
dataToSign, privSigningKeyHex, self.digestAlg)
@@ -1227,27 +1255,31 @@ class singleWorker(StoppableThread):
# We have assembled the data that will be encrypted.
try:
encrypted = highlevelcrypto.encrypt(
- payload, "04" + hexlify(pubEncryptionKeyBase256)
+ payload, b"04" + hexlify(pubEncryptionKeyBase256)
)
except: # noqa:E722
self.logger.warning("highlevelcrypto.encrypt didn't work")
- sqlExecute(
+ rowcount = sqlExecute(
'''UPDATE sent SET status='badkey' WHERE ackdata=? AND folder='sent' ''',
- ackdata
+ sqlite3.Binary(ackdata)
)
+ if rowcount < 1:
+ sqlExecute(
+ '''UPDATE sent SET status='badkey' WHERE ackdata=CAST(? AS TEXT) AND folder='sent' ''',
+ ackdata
+ )
queues.UISignalQueue.put((
'updateSentItemStatusByAckdata', (
- ackdata,
- tr._translate(
+ ackdata, _translate(
"MainWindow",
"Problem: The recipient\'s encryption key is"
- " no good. Could not encrypt message. %1"
- ).arg(l10n.formatTimestamp()))
+ " no good. Could not encrypt message. {0}"
+ ).format(l10n.formatTimestamp()))
))
continue
encryptedPayload = pack('>Q', embeddedTime)
- encryptedPayload += '\x00\x00\x00\x02' # object type: msg
+ encryptedPayload += b'\x00\x00\x00\x02' # object type: msg
encryptedPayload += encodeVarint(1) # msg version
encryptedPayload += encodeVarint(toStreamNumber) + encrypted
target = 2 ** 64 / (
@@ -1306,21 +1338,19 @@ class singleWorker(StoppableThread):
not protocol.checkBitfield(behaviorBitfield, protocol.BITFIELD_DOESACK):
queues.UISignalQueue.put((
'updateSentItemStatusByAckdata', (
- ackdata,
- tr._translate(
- "MainWindow",
- "Message sent. Sent at %1"
- ).arg(l10n.formatTimestamp()))))
+ ackdata, _translate(
+ "MainWindow", "Message sent. Sent at {0}"
+ ).format(l10n.formatTimestamp()))
+ ))
else:
# not sending to a chan or one of my addresses
queues.UISignalQueue.put((
'updateSentItemStatusByAckdata', (
- ackdata,
- tr._translate(
+ ackdata, _translate(
"MainWindow",
"Message sent. Waiting for acknowledgement."
- " Sent on %1"
- ).arg(l10n.formatTimestamp()))
+ " Sent on {0}"
+ ).format(l10n.formatTimestamp()))
))
self.logger.info(
'Broadcasting inv for my msg(within sendmsg function): %s',
@@ -1337,12 +1367,19 @@ class singleWorker(StoppableThread):
newStatus = 'msgsent'
# wait 10% past expiration
sleepTill = int(time.time() + TTL * 1.1)
- sqlExecute(
+ rowcount = sqlExecute(
'''UPDATE sent SET msgid=?, status=?, retrynumber=?, '''
''' sleeptill=?, lastactiontime=? WHERE ackdata=? AND folder='sent' ''',
- inventoryHash, newStatus, retryNumber + 1,
- sleepTill, int(time.time()), ackdata
+ sqlite3.Binary(inventoryHash), dbstr(newStatus), retryNumber + 1,
+ sleepTill, int(time.time()), sqlite3.Binary(ackdata)
)
+ if rowcount < 1:
+ sqlExecute(
+ '''UPDATE sent SET msgid=?, status=?, retrynumber=?, '''
+ ''' sleeptill=?, lastactiontime=? WHERE ackdata=CAST(? AS TEXT) AND folder='sent' ''',
+ sqlite3.Binary(inventoryHash), newStatus, retryNumber + 1,
+ sleepTill, int(time.time()), ackdata
+ )
# If we are sending to ourselves or a chan, let's put
# the message in our own inbox.
@@ -1386,7 +1423,7 @@ class singleWorker(StoppableThread):
'''SELECT retrynumber FROM sent WHERE toaddress=? '''
''' AND (status='doingpubkeypow' OR status='awaitingpubkey') '''
''' AND folder='sent' LIMIT 1''',
- toAddress
+ dbstr(toAddress)
)
if not queryReturn:
self.logger.critical(
@@ -1412,10 +1449,11 @@ class singleWorker(StoppableThread):
privEncryptionKey = doubleHashOfAddressData[:32]
# Note that this is the second half of the sha512 hash.
tag = doubleHashOfAddressData[32:]
- if tag not in state.neededPubkeys:
+ tag_bytes = bytes(tag)
+ if tag_bytes not in state.neededPubkeys:
# We'll need this for when we receive a pubkey reply:
# it will be encrypted and we'll need to decrypt it.
- state.neededPubkeys[tag] = (
+ state.neededPubkeys[tag_bytes] = (
toAddress,
highlevelcrypto.makeCryptor(hexlify(privEncryptionKey))
)
@@ -1429,7 +1467,7 @@ class singleWorker(StoppableThread):
TTL = TTL + helper_random.randomrandrange(-300, 300)
embeddedTime = int(time.time() + TTL)
payload = pack('>Q', embeddedTime)
- payload += '\x00\x00\x00\x00' # object type: getpubkey
+ payload += b'\x00\x00\x00\x00' # object type: getpubkey
payload += encodeVarint(addressVersionNumber)
payload += encodeVarint(streamNumber)
if addressVersionNumber <= 3:
@@ -1446,8 +1484,7 @@ class singleWorker(StoppableThread):
queues.UISignalQueue.put(('updateStatusBar', statusbar))
queues.UISignalQueue.put((
'updateSentItemStatusByToAddress', (
- toAddress,
- tr._translate(
+ toAddress, _translate(
"MainWindow",
"Doing work necessary to request encryption key."))
))
@@ -1468,33 +1505,34 @@ class singleWorker(StoppableThread):
''' status='awaitingpubkey', retrynumber=?, sleeptill=? '''
''' WHERE toaddress=? AND (status='doingpubkeypow' OR '''
''' status='awaitingpubkey') AND folder='sent' ''',
- int(time.time()), retryNumber + 1, sleeptill, toAddress)
+ int(time.time()), retryNumber + 1, sleeptill, dbstr(toAddress))
queues.UISignalQueue.put((
- 'updateStatusBar',
- tr._translate(
+ 'updateStatusBar', _translate(
"MainWindow",
"Broadcasting the public key request. This program will"
" auto-retry if they are offline.")
))
queues.UISignalQueue.put((
'updateSentItemStatusByToAddress', (
- toAddress,
- tr._translate(
+ toAddress, _translate(
"MainWindow",
"Sending public key request. Waiting for reply."
- " Requested at %1"
- ).arg(l10n.formatTimestamp()))
+ " Requested at {0}"
+ ).format(l10n.formatTimestamp()))
))
- def generateFullAckMessage(self, ackdata, _, TTL):
- """
- It might be perfectly fine to just use the same TTL for the ackdata that we use for the message. But I would
- rather it be more difficult for attackers to associate ackData with the associated msg object. However, users
- would want the TTL of the acknowledgement to be about the same as they set for the message itself. So let's set
- the TTL of the acknowledgement to be in one of three 'buckets': 1 hour, 7 days, or 28 days, whichever is
- relatively close to what the user specified.
- """
+ def generateFullAckMessage(self, ackdata, TTL):
+ """Create ACK packet"""
+ # It might be perfectly fine to just use the same TTL for
+ # the ackdata that we use for the message. But I would rather
+ # it be more difficult for attackers to associate ackData with
+ # the associated msg object. However, users would want the TTL
+ # of the acknowledgement to be about the same as they set
+ # for the message itself. So let's set the TTL of the
+ # acknowledgement to be in one of three 'buckets': 1 hour, 7
+ # days, or 28 days, whichever is relatively close to what the
+ # user specified.
if TTL < 24 * 60 * 60: # 1 day
TTL = 24 * 60 * 60 # 1 day
elif TTL < 7 * 24 * 60 * 60: # 1 week
@@ -1511,4 +1549,4 @@ class singleWorker(StoppableThread):
payload = self._doPOWDefaults(
payload, TTL, log_prefix='(For ack message)', log_time=True)
- return protocol.CreatePacket('object', payload)
+ return protocol.CreatePacket(b'object', payload)
diff --git a/src/class_smtpDeliver.py b/src/class_smtpDeliver.py
index 9e3b8ab3..490f296b 100644
--- a/src/class_smtpDeliver.py
+++ b/src/class_smtpDeliver.py
@@ -4,9 +4,9 @@ SMTP client thread for delivering emails
# pylint: disable=unused-variable
import smtplib
-import urlparse
+from six.moves.urllib import parse as urlparse
from email.header import Header
-from email.mime.text import MIMEText
+from six.moves import email_mime_text
import queues
import state
@@ -55,7 +55,7 @@ class smtpDeliver(StoppableThread):
u = urlparse.urlparse(dest)
to = urlparse.parse_qs(u.query)['to']
client = smtplib.SMTP(u.hostname, u.port)
- msg = MIMEText(body, 'plain', 'utf-8')
+ msg = email_mime_text(body, 'plain', 'utf-8')
msg['Subject'] = Header(subject, 'utf-8')
msg['From'] = fromAddress + '@' + SMTPDOMAIN
toLabel = map(
diff --git a/src/class_smtpServer.py b/src/class_smtpServer.py
index 44ea7c9c..40d1ed4a 100644
--- a/src/class_smtpServer.py
+++ b/src/class_smtpServer.py
@@ -12,6 +12,7 @@ import threading
import time
from email.header import decode_header
from email.parser import Parser
+import sqlite3
import queues
from addresses import decodeAddress
@@ -20,6 +21,7 @@ from helper_ackPayload import genAckPayload
from helper_sql import sqlExecute
from network.threads import StoppableThread
from version import softwareVersion
+from dbcompat import dbstr
SMTPDOMAIN = "bmaddr.lan"
LISTENPORT = 8425
@@ -88,19 +90,19 @@ class smtpServerPyBitmessage(smtpd.SMTPServer):
ackdata = genAckPayload(streamNumber, stealthLevel)
sqlExecute(
'''INSERT INTO sent VALUES (?,?,?,?,?,?,?,?,?,?,?,?,?,?,?)''',
- '',
- toAddress,
- ripe,
- fromAddress,
- subject,
- message,
- ackdata,
+ sqlite3.Binary(b''),
+ dbstr(toAddress),
+ sqlite3.Binary(ripe),
+ dbstr(fromAddress),
+ dbstr(subject),
+ dbstr(message),
+ sqlite3.Binary(ackdata),
int(time.time()), # sentTime (this will never change)
int(time.time()), # lastActionTime
0, # sleepTill time. This will get set when the POW gets done.
- 'msgqueued',
+ dbstr('msgqueued'),
0, # retryNumber
- 'sent', # folder
+ dbstr('sent'), # folder
2, # encodingtype
# not necessary to have a TTL higher than 2 days
min(config.getint('bitmessagesettings', 'ttl'), 86400 * 2)
diff --git a/src/class_sqlThread.py b/src/class_sqlThread.py
index 7df9e253..9b5e3a04 100644
--- a/src/class_sqlThread.py
+++ b/src/class_sqlThread.py
@@ -8,6 +8,7 @@ import sqlite3
import sys
import threading
import time
+from six.moves.reprlib import repr
try:
import helper_sql
@@ -38,7 +39,7 @@ class sqlThread(threading.Thread):
helper_sql.sql_available = True
config_ready.wait()
self.conn = sqlite3.connect(state.appdata + 'messages.dat')
- self.conn.text_factory = str
+ self.conn.text_factory = bytes
self.cur = self.conn.cursor()
self.cur.execute('PRAGMA secure_delete = true')
@@ -73,7 +74,7 @@ class sqlThread(threading.Thread):
'''INSERT INTO subscriptions VALUES'''
'''('Bitmessage new releases/announcements','BM-GtovgYdgs7qXPkoYaRgrLFuFKz1SFpsw',1)''')
self.cur.execute(
- '''CREATE TABLE settings (key blob, value blob, UNIQUE(key) ON CONFLICT REPLACE)''')
+ '''CREATE TABLE settings (key text, value blob, UNIQUE(key) ON CONFLICT REPLACE)''')
self.cur.execute('''INSERT INTO settings VALUES('version','11')''')
self.cur.execute('''INSERT INTO settings VALUES('lastvacuumtime',?)''', (
int(time.time()),))
@@ -542,7 +543,7 @@ class sqlThread(threading.Thread):
shutil.move(
paths.lookupAppdataFolder() + 'messages.dat', paths.lookupExeFolder() + 'messages.dat')
self.conn = sqlite3.connect(paths.lookupExeFolder() + 'messages.dat')
- self.conn.text_factory = str
+ self.conn.text_factory = bytes
self.cur = self.conn.cursor()
elif item == 'movemessagstoappdata':
logger.debug('the sqlThread is moving the messages.dat file to the Appdata folder.')
@@ -568,7 +569,7 @@ class sqlThread(threading.Thread):
shutil.move(
paths.lookupExeFolder() + 'messages.dat', paths.lookupAppdataFolder() + 'messages.dat')
self.conn = sqlite3.connect(paths.lookupAppdataFolder() + 'messages.dat')
- self.conn.text_factory = str
+ self.conn.text_factory = bytes
self.cur = self.conn.cursor()
elif item == 'deleteandvacuume':
self.cur.execute('''delete from inbox where folder='trash' ''')
diff --git a/src/dbcompat.py b/src/dbcompat.py
new file mode 100644
index 00000000..6a865630
--- /dev/null
+++ b/src/dbcompat.py
@@ -0,0 +1,22 @@
+import logging
+import six
+
+logger = logging.getLogger("default")
+
+def dbstr(v):
+ if six.PY3:
+ if isinstance(v, str):
+ return v
+ elif isinstance(v, bytes):
+ return v.decode("utf-8", "replace")
+ logger.debug("unexpected type in dbstr(): {}".format(type(v)))
+ return v # hope this never happens..
+ else: # assume six.PY2
+ if isinstance(v, unicode):
+ return v.encode("utf-8", "replace")
+ elif isinstance(v, str):
+ return v
+ elif isinstance(v, bytes):
+ return str(v)
+ logger.debug("unexpected type in dbstr(): {}".format(type(v)))
+ return v # hope this never happens..
diff --git a/src/depends.py b/src/depends.py
index d966d5fe..15722e63 100755
--- a/src/depends.py
+++ b/src/depends.py
@@ -6,6 +6,7 @@ and suggest how it may be installed
import os
import re
import sys
+import six
# Only really old versions of Python don't have sys.hexversion. We don't
# support them. The logging module was introduced in Python 2.3
@@ -17,6 +18,7 @@ if not hasattr(sys, 'hexversion') or sys.hexversion < 0x20300F0:
)
import logging # noqa:E402
+from distutils import version
import subprocess # nosec B404
from importlib import import_module
@@ -53,23 +55,23 @@ PACKAGE_MANAGER = {
}
PACKAGES = {
- "PyQt4": {
- "OpenBSD": "py-qt4",
- "FreeBSD": "py27-qt4",
- "Debian": "python-qt4",
- "Ubuntu": "python-qt4",
- "Ubuntu 12": "python-qt4",
- "Ubuntu 20": "",
- "openSUSE": "python-qt",
- "Fedora": "PyQt4",
- "Guix": "python2-pyqt@4.11.4",
- "Gentoo": "dev-python/PyQt4",
+ "qtpy": {
+ "OpenBSD": "py-qtpy",
+ "FreeBSD": "py27-QtPy",
+ "Debian": "python-qtpy",
+ "Ubuntu": "python-qtpy",
+ "Ubuntu 12": "python-qtpy",
+ "Ubuntu 20": "python-qtpy",
+ "openSUSE": "python-QtPy",
+ "Fedora": "python2-QtPy",
+ "Guix": "",
+ "Gentoo": "dev-python/QtPy",
"optional": True,
"description":
- "You only need PyQt if you want to use the GUI."
+ "You only need qtpy if you want to use the GUI."
" When only running as a daemon, this can be skipped.\n"
- "However, you would have to install it manually"
- " because setuptools does not support PyQt."
+ "Also maybe you need to install PyQt5 or PyQt4"
+ " if your package manager not installs it as qtpy dependency"
},
"msgpack": {
"OpenBSD": "py-msgpack",
@@ -156,19 +158,19 @@ detectOS.result = None
def detectOSRelease():
"""Detecting the release of OS"""
with open("/etc/os-release", 'r') as osRelease:
- version = None
+ ver = None
for line in osRelease:
if line.startswith("NAME="):
detectOS.result = OS_RELEASE.get(
line.replace('"', '').split("=")[-1].strip().lower())
elif line.startswith("VERSION_ID="):
try:
- version = float(line.split("=")[1].replace("\"", ""))
+ ver = float(line.split("=")[1].replace("\"", ""))
except ValueError:
pass
- if detectOS.result == "Ubuntu" and version < 14:
+ if detectOS.result == "Ubuntu" and ver < 14:
detectOS.result = "Ubuntu 12"
- elif detectOS.result == "Ubuntu" and version >= 20:
+ elif detectOS.result == "Ubuntu" and ver >= 20:
detectOS.result = "Ubuntu 20"
@@ -191,7 +193,7 @@ def try_import(module, log_extra=False):
def check_ripemd160():
"""Check availability of the RIPEMD160 hash function"""
try:
- from fallback import RIPEMD160Hash # pylint: disable=relative-import
+ from fallback import RIPEMD160Hash
except ImportError:
return False
return RIPEMD160Hash is not None
@@ -380,33 +382,71 @@ def check_curses():
def check_pyqt():
"""Do pyqt dependency check.
- Here we are checking for PyQt4 with its version, as for it require
- PyQt 4.8 or later.
+ Here we are checking for qtpy with its version, as for it require
+ qtpy.
"""
+ # pylint: disable=no-member
+ try:
+ import qtpy
+ except ImportError:
+ logger.error(
+ 'PyBitmessage requires qtpy, and PyQt5 or PyQt4, '
+ ' PyQt 4.8 or later and Qt 4.7 or later.')
+ return False
+
+ sip_found = False
+ try:
+ import sip
+ sip.setapi("QString", 2)
+ sip.setapi("QVariant", 2)
+ sip_found = True
+ except ImportError:
+ pass
+
QtCore = try_import(
- 'PyQt4.QtCore', 'PyBitmessage requires PyQt 4.8 or later and Qt 4.7 or later.')
+ 'qtpy.QtCore', 'PyBitmessage requires qtpy and Qt 4.7 or later.')
if not QtCore:
return False
- logger.info('PyQt Version: %s', QtCore.PYQT_VERSION_STR)
- logger.info('Qt Version: %s', QtCore.QT_VERSION_STR)
+ try:
+ logger.info('PyQt Version: %s', QtCore.PYQT_VERSION_STR)
+ except AttributeError:
+ logger.info('Can be PySide..')
+ try:
+ logger.info('Qt Version: %s', QtCore.__version__)
+ except AttributeError:
+ # Can be PySide..
+ pass
passed = True
- if QtCore.PYQT_VERSION < 0x40800:
- logger.error(
- 'This version of PyQt is too old. PyBitmessage requries'
- ' PyQt 4.8 or later.')
- passed = False
- if QtCore.QT_VERSION < 0x40700:
- logger.error(
- 'This version of Qt is too old. PyBitmessage requries'
- ' Qt 4.7 or later.')
- passed = False
+
+ try:
+ if version.LooseVersion(QtCore.PYQT_VERSION_STR) < '4.8':
+ logger.error(
+ 'This version of PyQt is too old. PyBitmessage requries'
+ ' PyQt 4.8 or later.')
+ passed = False
+ except AttributeError:
+ # Can be PySide..
+ pass
+ try:
+ if version.LooseVersion(QtCore.__version__) < '4.7':
+ logger.error(
+ 'This version of Qt is too old. PyBitmessage requries'
+ ' Qt 4.7 or later.')
+ passed = False
+ except AttributeError:
+ # Can be PySide..
+ pass
+
+ if passed and not sip_found:
+ logger.info("sip is not found although PyQt is found")
+ return False
+
return passed
-
def check_msgpack():
- """Do sgpack module check.
+ """Do msgpack module check.
simply checking if msgpack package with all its dependency
is available or not as recommended for messages coding.
@@ -435,14 +475,9 @@ def check_dependencies(verbose=False, optional=False):
logger.info('Python version: %s', sys.version)
if sys.hexversion < 0x20704F0:
logger.error(
- 'PyBitmessage requires Python 2.7.4 or greater'
- ' (but not Python 3+)')
+ 'PyBitmessage requires Python 2.7.4 or greater.'
+ ' Python 2.7.18 is recommended.')
has_all_dependencies = False
- if sys.hexversion >= 0x3000000:
- logger.error(
- 'PyBitmessage does not support Python 3+. Python 2.7.4'
- ' or greater is required. Python 2.7.18 is recommended.')
- sys.exit()
# FIXME: This needs to be uncommented when more of the code is python3 compatible
# if sys.hexversion >= 0x3000000 and sys.hexversion < 0x3060000:
diff --git a/src/fallback/umsgpack/umsgpack.py b/src/fallback/umsgpack/umsgpack.py
index 34938614..97bfdf4c 100644
--- a/src/fallback/umsgpack/umsgpack.py
+++ b/src/fallback/umsgpack/umsgpack.py
@@ -49,10 +49,12 @@ License: MIT
# pylint: disable=too-many-lines,too-many-branches,too-many-statements,global-statement,too-many-return-statements
# pylint: disable=unused-argument
-import collections
-import io
+from collections import OrderedDict
+from six.moves.collections_abc import Hashable
+from six.moves import range as xrange
import struct
import sys
+import six
__version__ = "2.4.1"
"Module version string"
@@ -99,9 +101,9 @@ class Ext: # pylint: disable=old-style-class
if not isinstance(type, int) or not (type >= 0 and type <= 127):
raise TypeError("ext type out of range")
# Check data is type bytes
- elif sys.version_info[0] == 3 and not isinstance(data, bytes):
+ elif six.PY3 and not isinstance(data, bytes):
raise TypeError("ext data is not type \'bytes\'")
- elif sys.version_info[0] == 2 and not isinstance(data, str):
+ elif six.PY2 and not isinstance(data, str):
raise TypeError("ext data is not type \'str\'")
self.type = type
self.data = data
@@ -125,7 +127,7 @@ class Ext: # pylint: disable=old-style-class
String representation of this Ext object.
"""
s = "Ext Object (Type: 0x%02x, Data: " % self.type
- s += " ".join(["0x%02x" % ord(self.data[i:i + 1])
+ s += " ".join(["0x%02x" % six.byte2int(self.data[i:i + 1])
for i in xrange(min(len(self.data), 8))])
if len(self.data) > 8:
s += " ..."
@@ -549,7 +551,7 @@ def _packb2(obj, **options):
'\x82\xa7compact\xc3\xa6schema\x00'
>>>
"""
- fp = io.BytesIO()
+ fp = six.BytesIO()
_pack2(obj, fp, **options)
return fp.getvalue()
@@ -582,7 +584,7 @@ def _packb3(obj, **options):
b'\x82\xa7compact\xc3\xa6schema\x00'
>>>
"""
- fp = io.BytesIO()
+ fp = six.BytesIO()
_pack3(obj, fp, **options)
return fp.getvalue()
@@ -599,7 +601,7 @@ def _read_except(fp, n):
def _unpack_integer(code, fp, options):
- if (ord(code) & 0xe0) == 0xe0:
+ if (six.byte2int(code) & 0xe0) == 0xe0:
return struct.unpack("b", code)[0]
elif code == b'\xd0':
return struct.unpack("b", _read_except(fp, 1))[0]
@@ -609,7 +611,7 @@ def _unpack_integer(code, fp, options):
return struct.unpack(">i", _read_except(fp, 4))[0]
elif code == b'\xd3':
return struct.unpack(">q", _read_except(fp, 8))[0]
- elif (ord(code) & 0x80) == 0x00:
+ elif (six.byte2int(code) & 0x80) == 0x00:
return struct.unpack("B", code)[0]
elif code == b'\xcc':
return struct.unpack("B", _read_except(fp, 1))[0]
@@ -619,21 +621,21 @@ def _unpack_integer(code, fp, options):
return struct.unpack(">I", _read_except(fp, 4))[0]
elif code == b'\xcf':
return struct.unpack(">Q", _read_except(fp, 8))[0]
- raise Exception("logic error, not int: 0x%02x" % ord(code))
+ raise Exception("logic error, not int: 0x%02x" % six.byte2int(code))
def _unpack_reserved(code, fp, options):
if code == b'\xc1':
raise ReservedCodeException(
- "encountered reserved code: 0x%02x" % ord(code))
+ "encountered reserved code: 0x%02x" % six.byte2int(code))
raise Exception(
- "logic error, not reserved code: 0x%02x" % ord(code))
+ "logic error, not reserved code: 0x%02x" % six.byte2int(code))
def _unpack_nil(code, fp, options):
if code == b'\xc0':
return None
- raise Exception("logic error, not nil: 0x%02x" % ord(code))
+ raise Exception("logic error, not nil: 0x%02x" % six.byte2int(code))
def _unpack_boolean(code, fp, options):
@@ -641,7 +643,7 @@ def _unpack_boolean(code, fp, options):
return False
elif code == b'\xc3':
return True
- raise Exception("logic error, not boolean: 0x%02x" % ord(code))
+ raise Exception("logic error, not boolean: 0x%02x" % six.byte2int(code))
def _unpack_float(code, fp, options):
@@ -649,12 +651,12 @@ def _unpack_float(code, fp, options):
return struct.unpack(">f", _read_except(fp, 4))[0]
elif code == b'\xcb':
return struct.unpack(">d", _read_except(fp, 8))[0]
- raise Exception("logic error, not float: 0x%02x" % ord(code))
+ raise Exception("logic error, not float: 0x%02x" % six.byte2int(code))
def _unpack_string(code, fp, options):
- if (ord(code) & 0xe0) == 0xa0:
- length = ord(code) & ~0xe0
+ if (six.byte2int(code) & 0xe0) == 0xa0:
+ length = six.byte2int(code) & ~0xe0
elif code == b'\xd9':
length = struct.unpack("B", _read_except(fp, 1))[0]
elif code == b'\xda':
@@ -662,7 +664,7 @@ def _unpack_string(code, fp, options):
elif code == b'\xdb':
length = struct.unpack(">I", _read_except(fp, 4))[0]
else:
- raise Exception("logic error, not string: 0x%02x" % ord(code))
+ raise Exception("logic error, not string: 0x%02x" % six.byte2int(code))
# Always return raw bytes in compatibility mode
global compatibility
@@ -686,7 +688,7 @@ def _unpack_binary(code, fp, options):
elif code == b'\xc6':
length = struct.unpack(">I", _read_except(fp, 4))[0]
else:
- raise Exception("logic error, not binary: 0x%02x" % ord(code))
+ raise Exception("logic error, not binary: 0x%02x" % six.byte2int(code))
return _read_except(fp, length)
@@ -709,9 +711,9 @@ def _unpack_ext(code, fp, options):
elif code == b'\xc9':
length = struct.unpack(">I", _read_except(fp, 4))[0]
else:
- raise Exception("logic error, not ext: 0x%02x" % ord(code))
+ raise Exception("logic error, not ext: 0x%02x" % six.byte2int(code))
- ext = Ext(ord(_read_except(fp, 1)), _read_except(fp, length))
+ ext = Ext(six.byte2int(_read_except(fp, 1)), _read_except(fp, length))
# Unpack with ext handler, if we have one
ext_handlers = options.get("ext_handlers")
@@ -722,14 +724,14 @@ def _unpack_ext(code, fp, options):
def _unpack_array(code, fp, options):
- if (ord(code) & 0xf0) == 0x90:
- length = (ord(code) & ~0xf0)
+ if (six.byte2int(code) & 0xf0) == 0x90:
+ length = (six.byte2int(code) & ~0xf0)
elif code == b'\xdc':
length = struct.unpack(">H", _read_except(fp, 2))[0]
elif code == b'\xdd':
length = struct.unpack(">I", _read_except(fp, 4))[0]
else:
- raise Exception("logic error, not array: 0x%02x" % ord(code))
+ raise Exception("logic error, not array: 0x%02x" % six.byte2int(code))
return [_unpack(fp, options) for _ in xrange(length)]
@@ -741,17 +743,17 @@ def _deep_list_to_tuple(obj):
def _unpack_map(code, fp, options):
- if (ord(code) & 0xf0) == 0x80:
- length = (ord(code) & ~0xf0)
+ if (six.byte2int(code) & 0xf0) == 0x80:
+ length = (six.byte2int(code) & ~0xf0)
elif code == b'\xde':
length = struct.unpack(">H", _read_except(fp, 2))[0]
elif code == b'\xdf':
length = struct.unpack(">I", _read_except(fp, 4))[0]
else:
- raise Exception("logic error, not map: 0x%02x" % ord(code))
+ raise Exception("logic error, not map: 0x%02x" % six.byte2int(code))
d = {} if not options.get('use_ordered_dict') \
- else collections.OrderedDict()
+ else OrderedDict()
for _ in xrange(length):
# Unpack key
k = _unpack(fp, options)
@@ -759,7 +761,7 @@ def _unpack_map(code, fp, options):
if isinstance(k, list):
# Attempt to convert list into a hashable tuple
k = _deep_list_to_tuple(k)
- elif not isinstance(k, collections.Hashable):
+ elif not isinstance(k, Hashable):
raise UnhashableKeyException(
"encountered unhashable key: %s, %s" % (str(k), str(type(k))))
elif k in d:
@@ -911,7 +913,7 @@ def _unpackb2(s, **options):
"""
if not isinstance(s, (str, bytearray)):
raise TypeError("packed data must be type 'str' or 'bytearray'")
- return _unpack(io.BytesIO(s), options)
+ return _unpack(six.BytesIO(s), options)
# For Python 3, expects a bytes object
@@ -957,7 +959,7 @@ def _unpackb3(s, **options):
"""
if not isinstance(s, (bytes, bytearray)):
raise TypeError("packed data must be type 'bytes' or 'bytearray'")
- return _unpack(io.BytesIO(s), options)
+ return _unpack(six.BytesIO(s), options)
#############################################################################
# Module Initialization
@@ -990,7 +992,7 @@ def __init():
_float_precision = "single"
# Map packb and unpackb to the appropriate version
- if sys.version_info[0] == 3:
+ if six.PY3:
pack = _pack3
packb = _packb3
dump = _pack3
diff --git a/src/helper_addressbook.py b/src/helper_addressbook.py
index 6d354113..6f1d40d2 100644
--- a/src/helper_addressbook.py
+++ b/src/helper_addressbook.py
@@ -4,11 +4,12 @@ Insert value into addressbook
from bmconfigparser import config
from helper_sql import sqlExecute
+from dbcompat import dbstr
def insert(address, label):
"""perform insert into addressbook"""
if address not in config.addresses():
- return sqlExecute('''INSERT INTO addressbook VALUES (?,?)''', label, address) == 1
+ return sqlExecute('''INSERT INTO addressbook VALUES (?,?)''', dbstr(label), dbstr(address)) == 1
return False
diff --git a/src/helper_bitcoin.py b/src/helper_bitcoin.py
index d4f1d105..a51eaf9c 100644
--- a/src/helper_bitcoin.py
+++ b/src/helper_bitcoin.py
@@ -19,17 +19,17 @@ def calculateBitcoinAddressFromPubkey(pubkey):
sha = hashlib.new('sha256')
sha.update(pubkey)
ripe.update(sha.digest())
- ripeWithProdnetPrefix = '\x00' + ripe.digest()
+ ripeWithProdnetPrefix = b'\x00' + ripe.digest()
checksum = hashlib.sha256(hashlib.sha256(
ripeWithProdnetPrefix).digest()).digest()[:4]
binaryBitcoinAddress = ripeWithProdnetPrefix + checksum
numberOfZeroBytesOnBinaryBitcoinAddress = 0
- while binaryBitcoinAddress[0] == '\x00':
+ while binaryBitcoinAddress[0] == b'\x00':
numberOfZeroBytesOnBinaryBitcoinAddress += 1
binaryBitcoinAddress = binaryBitcoinAddress[1:]
base58encoded = arithmetic.changebase(binaryBitcoinAddress, 256, 58)
- return "1" * numberOfZeroBytesOnBinaryBitcoinAddress + base58encoded
+ return b"1" * numberOfZeroBytesOnBinaryBitcoinAddress + base58encoded
def calculateTestnetAddressFromPubkey(pubkey):
@@ -43,14 +43,14 @@ def calculateTestnetAddressFromPubkey(pubkey):
sha = hashlib.new('sha256')
sha.update(pubkey)
ripe.update(sha.digest())
- ripeWithProdnetPrefix = '\x6F' + ripe.digest()
+ ripeWithProdnetPrefix = b'\x6F' + ripe.digest()
checksum = hashlib.sha256(hashlib.sha256(
ripeWithProdnetPrefix).digest()).digest()[:4]
binaryBitcoinAddress = ripeWithProdnetPrefix + checksum
numberOfZeroBytesOnBinaryBitcoinAddress = 0
- while binaryBitcoinAddress[0] == '\x00':
+ while binaryBitcoinAddress[0] == b'\x00':
numberOfZeroBytesOnBinaryBitcoinAddress += 1
binaryBitcoinAddress = binaryBitcoinAddress[1:]
base58encoded = arithmetic.changebase(binaryBitcoinAddress, 256, 58)
- return "1" * numberOfZeroBytesOnBinaryBitcoinAddress + base58encoded
+ return b"1" * numberOfZeroBytesOnBinaryBitcoinAddress + base58encoded
diff --git a/src/helper_inbox.py b/src/helper_inbox.py
index 555795df..b981fd2b 100644
--- a/src/helper_inbox.py
+++ b/src/helper_inbox.py
@@ -1,12 +1,16 @@
"""Helper Inbox performs inbox messages related operations"""
+import sqlite3
+
import queues
from helper_sql import sqlExecute, sqlQuery
+from dbcompat import dbstr
def insert(t):
"""Perform an insert into the "inbox" table"""
- sqlExecute('''INSERT INTO inbox VALUES (?,?,?,?,?,?,?,?,?,?)''', *t)
+ u = [sqlite3.Binary(t[0]), dbstr(t[1]), dbstr(t[2]), dbstr(t[3]), dbstr(t[4]), dbstr(t[5]), dbstr(t[6]), t[7], t[8], sqlite3.Binary(t[9])]
+ sqlExecute('''INSERT INTO inbox VALUES (?,?,?,?,?,?,?,?,?,?)''', *u)
# shouldn't emit changedInboxUnread and displayNewInboxMessage
# at the same time
# queues.UISignalQueue.put(('changedInboxUnread', None))
@@ -14,22 +18,31 @@ def insert(t):
def trash(msgid):
"""Mark a message in the `inbox` as `trash`"""
- sqlExecute('''UPDATE inbox SET folder='trash' WHERE msgid=?''', msgid)
+ rowcount = sqlExecute('''UPDATE inbox SET folder='trash' WHERE msgid=?''', sqlite3.Binary(msgid))
+ if rowcount < 1:
+ sqlExecute('''UPDATE inbox SET folder='trash' WHERE msgid=CAST(? AS TEXT)''', msgid)
queues.UISignalQueue.put(('removeInboxRowByMsgid', msgid))
def delete(ack_data):
"""Permanent delete message from trash"""
- sqlExecute("DELETE FROM inbox WHERE msgid = ?", ack_data)
+ rowcount = sqlExecute("DELETE FROM inbox WHERE msgid = ?", sqlite3.Binary(ack_data))
+ if rowcount < 1:
+ sqlExecute("DELETE FROM inbox WHERE msgid = CAST(? AS TEXT)", ack_data)
def undeleteMessage(msgid):
"""Undelte the message"""
- sqlExecute('''UPDATE inbox SET folder='inbox' WHERE msgid=?''', msgid)
+ rowcount = sqlExecute('''UPDATE inbox SET folder='inbox' WHERE msgid=?''', sqlite3.Binary(msgid))
+ if rowcount < 1:
+ sqlExecute('''UPDATE inbox SET folder='inbox' WHERE msgid=CAST(? AS TEXT)''', msgid)
def isMessageAlreadyInInbox(sigHash):
"""Check for previous instances of this message"""
queryReturn = sqlQuery(
- '''SELECT COUNT(*) FROM inbox WHERE sighash=?''', sigHash)
+ '''SELECT COUNT(*) FROM inbox WHERE sighash=?''', sqlite3.Binary(sigHash))
+ if len(queryReturn) < 1:
+ queryReturn = sqlQuery(
+ '''SELECT COUNT(*) FROM inbox WHERE sighash=CAST(? AS TEXT)''', sigHash)
return queryReturn[0][0] != 0
diff --git a/src/helper_msgcoding.py b/src/helper_msgcoding.py
index 05fa1c1b..daa4ebac 100644
--- a/src/helper_msgcoding.py
+++ b/src/helper_msgcoding.py
@@ -2,7 +2,6 @@
Message encoding end decoding functions
"""
-import string
import zlib
import messagetypes
@@ -71,7 +70,8 @@ class MsgEncode(object):
def encodeSimple(self, message):
"""Handle simple encoding"""
- self.data = 'Subject:%(subject)s\nBody:%(body)s' % message
+ data = 'Subject:%(subject)s\nBody:%(body)s' % message
+ self.data = data.encode("utf-8", "replace")
self.length = len(self.data)
def encodeTrivial(self, message):
@@ -99,14 +99,14 @@ class MsgDecode(object):
def decodeExtended(self, data):
"""Handle extended encoding"""
dc = zlib.decompressobj()
- tmp = ""
+ tmp = b""
while len(tmp) <= config.safeGetInt("zlib", "maxsize"):
try:
got = dc.decompress(
data, config.safeGetInt("zlib", "maxsize")
+ 1 - len(tmp))
# EOF
- if got == "":
+ if got == b"":
break
tmp += got
data = dc.unconsumed_tail
@@ -142,7 +142,7 @@ class MsgDecode(object):
def decodeSimple(self, data):
"""Handle simple encoding"""
- bodyPositionIndex = string.find(data, '\nBody:')
+ bodyPositionIndex = data.find(b'\nBody:')
if bodyPositionIndex > 1:
subject = data[8:bodyPositionIndex]
# Only save and show the first 500 characters of the subject.
@@ -150,10 +150,11 @@ class MsgDecode(object):
subject = subject[:500]
body = data[bodyPositionIndex + 6:]
else:
- subject = ''
+ subject = b''
body = data
# Throw away any extra lines (headers) after the subject.
if subject:
subject = subject.splitlines()[0]
- self.subject = subject
- self.body = body
+ # Field types should be the same for all message types
+ self.subject = subject.decode("utf-8", "replace")
+ self.body = body.decode("utf-8", "replace")
diff --git a/src/helper_search.py b/src/helper_search.py
index 9fcb88b5..46cc4e80 100644
--- a/src/helper_search.py
+++ b/src/helper_search.py
@@ -5,6 +5,7 @@ Used by :mod:`.bitmessageqt`.
from helper_sql import sqlQuery
from tr import _translate
+from dbcompat import dbstr
def search_sql(
@@ -52,23 +53,23 @@ def search_sql(
if account is not None:
if xAddress == 'both':
sqlStatementParts.append('(fromaddress = ? OR toaddress = ?)')
- sqlArguments.append(account)
- sqlArguments.append(account)
+ sqlArguments.append(dbstr(account))
+ sqlArguments.append(dbstr(account))
else:
sqlStatementParts.append(xAddress + ' = ? ')
- sqlArguments.append(account)
+ sqlArguments.append(dbstr(account))
if folder is not None:
if folder == 'new':
folder = 'inbox'
unreadOnly = True
sqlStatementParts.append('folder = ? ')
- sqlArguments.append(folder)
+ sqlArguments.append(dbstr(folder))
else:
sqlStatementParts.append('folder != ?')
- sqlArguments.append('trash')
+ sqlArguments.append(dbstr('trash'))
if what:
sqlStatementParts.append('%s LIKE ?' % (where))
- sqlArguments.append(what)
+ sqlArguments.append(dbstr(what))
if unreadOnly:
sqlStatementParts.append('read = 0')
if sqlStatementParts:
diff --git a/src/helper_sent.py b/src/helper_sent.py
index aa76e756..14138d00 100644
--- a/src/helper_sent.py
+++ b/src/helper_sent.py
@@ -4,10 +4,12 @@ Insert values into sent table
import time
import uuid
+import sqlite3
from addresses import decodeAddress
from bmconfigparser import config
from helper_ackPayload import genAckPayload
from helper_sql import sqlExecute, sqlQuery
+from dbcompat import dbstr
# pylint: disable=too-many-arguments
@@ -38,8 +40,8 @@ def insert(msgid=None, toAddress='[Broadcast subscribers]', fromAddress=None, su
ttl = ttl if ttl else config.getint('bitmessagesettings', 'ttl')
- t = (msgid, toAddress, ripe, fromAddress, subject, message, ackdata,
- sentTime, lastActionTime, sleeptill, status, retryNumber, folder,
+ t = (sqlite3.Binary(msgid), dbstr(toAddress), sqlite3.Binary(ripe), dbstr(fromAddress), dbstr(subject), dbstr(message), sqlite3.Binary(ackdata),
+ sentTime, lastActionTime, sleeptill, dbstr(status), retryNumber, dbstr(folder),
encoding, ttl)
sqlExecute('''INSERT INTO sent VALUES (?,?,?,?,?,?,?,?,?,?,?,?,?,?,?)''', *t)
@@ -50,20 +52,30 @@ def insert(msgid=None, toAddress='[Broadcast subscribers]', fromAddress=None, su
def delete(ack_data):
"""Perform Delete query"""
- sqlExecute("DELETE FROM sent WHERE ackdata = ?", ack_data)
+ rowcount = sqlExecute("DELETE FROM sent WHERE ackdata = ?", sqlite3.Binary(ack_data))
+ if rowcount < 1:
+ sqlExecute("DELETE FROM sent WHERE ackdata = CAST(? AS TEXT)", ack_data)
def retrieve_message_details(ack_data):
"""Retrieving Message details"""
data = sqlQuery(
- "select toaddress, fromaddress, subject, message, received from inbox where msgid = ?", ack_data
+ "select toaddress, fromaddress, subject, message, received from inbox where msgid = ?", sqlite3.Binary(ack_data)
)
+ if len(data) < 1:
+ data = sqlQuery(
+ "select toaddress, fromaddress, subject, message, received from inbox where msgid = CAST(? AS TEXT)", ack_data
+ )
return data
def trash(ackdata):
"""Mark a message in the `sent` as `trash`"""
rowcount = sqlExecute(
- '''UPDATE sent SET folder='trash' WHERE ackdata=?''', ackdata
+ '''UPDATE sent SET folder='trash' WHERE ackdata=?''', sqlite3.Binary(ackdata)
)
+ if rowcount < 1:
+ rowcount = sqlExecute(
+ '''UPDATE sent SET folder='trash' WHERE ackdata=CAST(? AS TEXT)''', ackdata
+ )
return rowcount
diff --git a/src/helper_sql.py b/src/helper_sql.py
index 8dee9e0c..c5f30851 100644
--- a/src/helper_sql.py
+++ b/src/helper_sql.py
@@ -61,7 +61,7 @@ def sqlQuery(sql_statement, *args):
return queryreturn
-def sqlExecuteChunked(sql_statement, idCount, *args):
+def sqlExecuteChunked(sql_statement, as_text, idCount, *args):
"""Execute chunked SQL statement to avoid argument limit"""
# SQLITE_MAX_VARIABLE_NUMBER,
# unfortunately getting/setting isn't exposed to python
@@ -80,9 +80,18 @@ def sqlExecuteChunked(sql_statement, idCount, *args):
chunk_slice = args[
i:i + sqlExecuteChunked.chunkSize - (len(args) - idCount)
]
- sqlSubmitQueue.put(
- sql_statement.format(','.join('?' * len(chunk_slice)))
- )
+ if as_text:
+ q = ""
+ n = len(chunk_slice)
+ for i in range(n):
+ q += "CAST(? AS TEXT)"
+ if i != n - 1:
+ q += ","
+ sqlSubmitQueue.put(sql_statement.format(q))
+ else:
+ sqlSubmitQueue.put(
+ sql_statement.format(','.join('?' * len(chunk_slice)))
+ )
# first static args, and then iterative chunk
sqlSubmitQueue.put(
args[0:len(args) - idCount] + chunk_slice
diff --git a/src/highlevelcrypto.py b/src/highlevelcrypto.py
index b83da2f3..d6575a5f 100644
--- a/src/highlevelcrypto.py
+++ b/src/highlevelcrypto.py
@@ -7,6 +7,8 @@ High level cryptographic functions based on `.pyelliptic` OpenSSL bindings.
`More discussion. `_
"""
+from unqstr import unic
+
import hashlib
import os
from binascii import hexlify
@@ -102,7 +104,7 @@ def random_keys():
def deterministic_keys(passphrase, nonce):
"""Generate keys from *passphrase* and *nonce* (encoded as varint)"""
- priv = hashlib.sha512(passphrase + nonce).digest()[:32]
+ priv = hashlib.sha512(unic(passphrase).encode("utf-8", "replace") + nonce).digest()[:32]
pub = pointMult(priv)
return priv, pub
diff --git a/src/inventory.py b/src/inventory.py
index 5b739e84..8356262c 100644
--- a/src/inventory.py
+++ b/src/inventory.py
@@ -28,8 +28,6 @@ class Inventory:
# cheap inheritance copied from asyncore
def __getattr__(self, attr):
- if attr == "__contains__":
- self.numberOfInventoryLookupsPerformed += 1
try:
realRet = getattr(self._realInventory, attr)
except AttributeError:
@@ -40,6 +38,10 @@ class Inventory:
else:
return realRet
+ def __contains__(self, key):
+ self.numberOfInventoryLookupsPerformed += 1
+ return key in self._realInventory
+
# hint for pylint: this is dictionary like object
def __getitem__(self, key):
return self._realInventory[key]
diff --git a/src/l10n.py b/src/l10n.py
index fe02d3f4..927937d7 100644
--- a/src/l10n.py
+++ b/src/l10n.py
@@ -3,8 +3,8 @@
import logging
import os
import re
-import sys
import time
+import six
from six.moves import range
@@ -61,7 +61,7 @@ if not re.search(r'\d', time.strftime(time_format)):
# It seems some systems lie about the encoding they use
# so we perform comprehensive decoding tests
-elif sys.version_info[0] == 2:
+elif six.PY2:
try:
# Check day names
for i in range(7):
@@ -118,7 +118,7 @@ def formatTimestamp(timestamp=None):
except ValueError:
timestring = time.strftime(time_format)
- if sys.version_info[0] == 2:
+ if six.PY2:
return timestring.decode(encoding)
return timestring
diff --git a/src/namecoin.py b/src/namecoin.py
index a16cb3d7..3507f9ab 100644
--- a/src/namecoin.py
+++ b/src/namecoin.py
@@ -1,10 +1,10 @@
"""
Namecoin queries
"""
-# pylint: disable=too-many-branches,protected-access
+# pylint: disable=too-many-branches
import base64
-import httplib
+from six.moves import http_client as httplib
import json
import os
import socket
@@ -14,14 +14,14 @@ import defaults
from addresses import decodeAddress
from bmconfigparser import config
from debug import logger
-from tr import _translate # translate
+from tr import _translate
+
configSection = "bitmessagesettings"
class RPCError(Exception):
"""Error thrown when the RPC call returns an error."""
-
error = None
def __init__(self, data):
@@ -29,7 +29,7 @@ class RPCError(Exception):
self.error = data
def __str__(self):
- return "{0}: {1}".format(type(self).__name__, self.error)
+ return '{0}: {1}'.format(type(self).__name__, self.error)
class namecoinConnection(object):
@@ -46,8 +46,8 @@ class namecoinConnection(object):
def __init__(self, options=None):
"""
- Initialise. If options are given, take the connection settings from
- them instead of loading from the configs. This can be used to test
+ Initialise. If options are given, take the connection settings from
+ them instead of loading from the configs. This can be used to test
currently entered connection settings in the config dialog without
actually changing the values (yet).
"""
@@ -69,14 +69,14 @@ class namecoinConnection(object):
self.user = options["user"]
self.password = options["password"]
- assert self.nmctype == "namecoind" or self.nmctype == "nmcontrol"
+ assert self.nmctype in ("namecoind", "nmcontrol")
if self.nmctype == "namecoind":
self.con = httplib.HTTPConnection(self.host, self.port, timeout=3)
def query(self, identity):
"""
Query for the bitmessage address corresponding to the given identity
- string. If it doesn't contain a slash, id/ is prepended. We return
+ string. If it doesn't contain a slash, id/ is prepended. We return
the result as (Error, Address) pair, where the Error is an error
message to display or None in case of success.
"""
@@ -96,8 +96,8 @@ class namecoinConnection(object):
res = res["reply"]
if not res:
return (_translate(
- "MainWindow", "The name %1 was not found."
- ).arg(identity.decode("utf-8", "ignore")), None)
+ "MainWindow", "The name {0} was not found."
+ ).format(identity.decode("utf-8", "ignore")), None)
else:
assert False
except RPCError as exc:
@@ -107,12 +107,12 @@ class namecoinConnection(object):
else:
errmsg = exc.error
return (_translate(
- "MainWindow", "The namecoin query failed (%1)"
- ).arg(errmsg.decode("utf-8", "ignore")), None)
+ "MainWindow", "The namecoin query failed ({0})"
+ ).format(errmsg.decode("utf-8", "ignore")), None)
except AssertionError:
return (_translate(
- "MainWindow", "Unknown namecoin interface type: %1"
- ).arg(self.nmctype.decode("utf-8", "ignore")), None)
+ "MainWindow", "Unknown namecoin interface type: {0}"
+ ).format(self.nmctype.decode("utf-8", "ignore")), None)
except Exception:
logger.exception("Namecoin query exception")
return (_translate(
@@ -135,12 +135,12 @@ class namecoinConnection(object):
) if valid else (
_translate(
"MainWindow",
- "The name %1 has no associated Bitmessage address."
- ).arg(identity.decode("utf-8", "ignore")), None)
+ "The name {0} has no associated Bitmessage address."
+ ).format(identity.decode("utf-8", "ignore")), None)
def test(self):
"""
- Test the connection settings. This routine tries to query a "getinfo"
+ Test the connection settings. This routine tries to query a "getinfo"
command, and builds either an error message or a success message with
some info from it.
"""
@@ -160,11 +160,10 @@ class namecoinConnection(object):
versStr = "0.%d.%d" % (v1, v2)
else:
versStr = "0.%d.%d.%d" % (v1, v2, v3)
- message = (
- "success",
- _translate(
+ return (
+ 'success', _translate(
"MainWindow",
- "Success! Namecoind version %1 running.").arg(
+ "Success! Namecoind version {0} running.").format(
versStr.decode("utf-8", "ignore")))
elif self.nmctype == "nmcontrol":
@@ -172,32 +171,24 @@ class namecoinConnection(object):
prefix = "Plugin data running"
if ("reply" in res) and res["reply"][:len(prefix)] == prefix:
return (
- "success",
- _translate(
+ 'success', _translate(
"MainWindow",
- "Success! NMControll is up and running."
- )
+ "Success! NMControll is up and running.")
)
logger.error("Unexpected nmcontrol reply: %s", res)
- message = (
- "failed",
- _translate(
- "MainWindow",
- "Couldn\'t understand NMControl."
- )
+ return (
+ 'failed', _translate(
+ "MainWindow", "Couldn\'t understand NMControl.")
)
else:
sys.exit("Unsupported Namecoin type")
- return message
-
except Exception:
logger.info("Namecoin connection test failure")
return (
- "failed",
- _translate(
+ 'failed', _translate(
"MainWindow", "The connection to namecoin failed.")
)
@@ -245,26 +236,24 @@ class namecoinConnection(object):
"Authorization", "Basic %s" % base64.b64encode(authstr))
self.con.endheaders()
self.con.send(data)
+ try:
+ resp = self.con.getresponse()
+ result = resp.read()
+ if resp.status != 200:
+ raise Exception(
+ "Namecoin returned status %i: %s" %
+ (resp.status, resp.reason))
+ except: # noqa:E722
+ logger.info("HTTP receive error")
except: # noqa:E722
logger.info("HTTP connection error")
- return None
-
- try:
- resp = self.con.getresponse()
- result = resp.read()
- if resp.status != 200:
- raise Exception(
- "Namecoin returned status"
- " %i: %s" % (resp.status, resp.reason))
- except: # noqa:E722
- logger.info("HTTP receive error")
- return None
return result
def queryServer(self, data):
- """Helper routine sending data to the RPC "
- "server and returning the result."""
+ """
+ Helper routine sending data to the RPC server and returning the result.
+ """
try:
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
@@ -296,23 +285,24 @@ def lookupNamecoinFolder():
"""
app = "namecoin"
- from os import path, environ
+
if sys.platform == "darwin":
- if "HOME" in environ:
- dataFolder = path.join(os.environ["HOME"],
- "Library/Application Support/", app) + "/"
- else:
+ try:
+ dataFolder = os.path.join(
+ os.getenv("HOME"), "Library/Application Support/", app)
+ except TypeError: # getenv is None
sys.exit(
"Could not find home folder, please report this message"
" and your OS X version to the BitMessage Github."
- )
+ ) # TODO: remove exits from utility modules
- elif "win32" in sys.platform or "win64" in sys.platform:
- dataFolder = path.join(environ["APPDATA"], app) + "\\"
- else:
- dataFolder = path.join(environ["HOME"], ".%s" % app) + "/"
+ dataFolder = (
+ os.path.join(os.getenv("APPDATA"), app)
+ if sys.platform.startswith('win') else
+ os.path.join(os.getenv("HOME"), ".%s" % app)
+ )
- return dataFolder
+ return dataFolder + os.path.sep
def ensureNamecoinOptions():
@@ -357,8 +347,8 @@ def ensureNamecoinOptions():
nmc.close()
except IOError:
logger.warning(
- "%s unreadable or missing, Namecoin support deactivated",
- nmcConfig)
+ "%s unreadable or missing, Namecoin support deactivated", nmcConfig
+ )
except Exception:
logger.warning("Error processing namecoin.conf", exc_info=True)
diff --git a/src/network/__init__.py b/src/network/__init__.py
index c87ad64d..1e34d0b7 100644
--- a/src/network/__init__.py
+++ b/src/network/__init__.py
@@ -20,7 +20,7 @@ __all__ = ["StoppableThread"]
def start(config, state):
"""Start network threads"""
from .announcethread import AnnounceThread
- import connectionpool # pylint: disable=relative-import
+ from network import connectionpool
from .addrthread import AddrThread
from .downloadthread import DownloadThread
from .invthread import InvThread
diff --git a/src/network/addrthread.py b/src/network/addrthread.py
index 81e44506..68a0307f 100644
--- a/src/network/addrthread.py
+++ b/src/network/addrthread.py
@@ -5,11 +5,11 @@ import random
from six.moves import queue
# magic imports!
-import connectionpool
+from network import connectionpool
from protocol import assembleAddrMessage
from network import addrQueue # FIXME: init with queue
-from threads import StoppableThread
+from .threads import StoppableThread
class AddrThread(StoppableThread):
diff --git a/src/network/advanceddispatcher.py b/src/network/advanceddispatcher.py
index 49f0d19d..33c0c12e 100644
--- a/src/network/advanceddispatcher.py
+++ b/src/network/advanceddispatcher.py
@@ -7,7 +7,7 @@ import time
import network.asyncore_pollchoose as asyncore
import state
-from threads import BusyError, nonBlocking
+from .threads import BusyError, nonBlocking
class ProcessingError(Exception):
diff --git a/src/network/announcethread.py b/src/network/announcethread.py
index 7cb35e77..77c7c63a 100644
--- a/src/network/announcethread.py
+++ b/src/network/announcethread.py
@@ -4,12 +4,12 @@ Announce myself (node address)
import time
# magic imports!
-import connectionpool
+from network import connectionpool
from bmconfigparser import config
from protocol import assembleAddrMessage
-from node import Peer
-from threads import StoppableThread
+from .node import Peer
+from .threads import StoppableThread
class AnnounceThread(StoppableThread):
diff --git a/src/network/asyncore_pollchoose.py b/src/network/asyncore_pollchoose.py
index 9d0ffc1b..a1d34878 100644
--- a/src/network/asyncore_pollchoose.py
+++ b/src/network/asyncore_pollchoose.py
@@ -19,6 +19,7 @@ from errno import (
ENOTCONN, ENOTSOCK, EPIPE, ESHUTDOWN, ETIMEDOUT, EWOULDBLOCK, errorcode
)
from threading import current_thread
+from six.moves.reprlib import repr
try:
@@ -723,21 +724,6 @@ class dispatcher(object):
if why.args[0] not in (ENOTCONN, EBADF):
raise
- # cheap inheritance, used to pass all other attribute
- # references to the underlying socket object.
- def __getattr__(self, attr):
- try:
- retattr = getattr(self.socket, attr)
- except AttributeError:
- raise AttributeError(
- "%s instance has no attribute '%s'"
- % (self.__class__.__name__, attr))
- else:
- msg = "%(me)s.%(attr)s is deprecated; use %(me)s.socket.%(attr)s"\
- " instead" % {'me': self.__class__.__name__, 'attr': attr}
- warnings.warn(msg, DeprecationWarning, stacklevel=2)
- return retattr
-
# log and log_info may be overridden to provide more sophisticated
# logging and warning methods. In general, log is for 'hit' logging
# and 'log_info' is for informational, warning and error logging.
diff --git a/src/network/bmobject.py b/src/network/bmobject.py
index 83311b9b..cc34231e 100644
--- a/src/network/bmobject.py
+++ b/src/network/bmobject.py
@@ -6,7 +6,7 @@ import time
import protocol
import state
-import connectionpool
+import network.connectionpool # use long name to address recursive import
from network import dandelion_ins
from highlevelcrypto import calculateInventoryHash
@@ -100,7 +100,7 @@ class BMObject(object): # pylint: disable=too-many-instance-attributes
logger.warning(
'The object has invalid stream: %s', self.streamNumber)
raise BMObjectInvalidError()
- if self.streamNumber not in connectionpool.pool.streams:
+ if self.streamNumber not in network.connectionpool.pool.streams:
logger.debug(
'The streamNumber %i isn\'t one we are interested in.',
self.streamNumber)
diff --git a/src/network/bmproto.py b/src/network/bmproto.py
index e5f38bf5..13b1ca44 100644
--- a/src/network/bmproto.py
+++ b/src/network/bmproto.py
@@ -9,13 +9,14 @@ import re
import socket
import struct
import time
+import six
# magic imports!
import addresses
-import knownnodes
+from network import knownnodes
import protocol
import state
-import connectionpool
+import network.connectionpool # use long name to address recursive import
from bmconfigparser import config
from queues import objectProcessorQueue
from randomtrackingdict import RandomTrackingDict
@@ -26,14 +27,27 @@ from network.bmobject import (
BMObjectUnwantedStreamError
)
from network.proxy import ProxyError
+
from network import dandelion_ins, invQueue, portCheckerQueue
-from node import Node, Peer
-from objectracker import ObjectTracker, missingObjects
+from .node import Node, Peer
+from .objectracker import ObjectTracker, missingObjects
logger = logging.getLogger('default')
+def _hoststr(v):
+ if six.PY3:
+ return v
+ else: # assume six.PY2
+ return str(v)
+
+def _restr(v):
+ if six.PY3:
+ return v.decode("utf-8", "replace")
+ else: # assume six.PY2
+ return v
+
class BMProtoError(ProxyError):
"""A Bitmessage Protocol Base Error"""
errorCodes = ("Protocol error")
@@ -82,7 +96,7 @@ class BMProto(AdvancedDispatcher, ObjectTracker):
"""Process incoming header"""
self.magic, self.command, self.payloadLength, self.checksum = \
protocol.Header.unpack(self.read_buf[:protocol.Header.size])
- self.command = self.command.rstrip('\x00')
+ self.command = self.command.rstrip(b'\x00')
if self.magic != protocol.magic:
# skip 1 byte in order to sync
self.set_state("bm_header", length=1)
@@ -107,7 +121,7 @@ class BMProto(AdvancedDispatcher, ObjectTracker):
self.invalid = True
retval = True
if not self.fullyEstablished and self.command not in (
- "error", "version", "verack"):
+ b"error", b"version", b"verack"):
logger.error(
'Received command %s before connection was fully'
' established, ignoring', self.command)
@@ -115,7 +129,7 @@ class BMProto(AdvancedDispatcher, ObjectTracker):
if not self.invalid:
try:
retval = getattr(
- self, "bm_command_" + str(self.command).lower())()
+ self, "bm_command_" + self.command.decode("utf-8", "replace").lower())()
except AttributeError:
# unimplemented command
logger.debug('unimplemented command %s', self.command)
@@ -168,17 +182,17 @@ class BMProto(AdvancedDispatcher, ObjectTracker):
"""Decode node details from the payload"""
# protocol.checkIPAddress()
services, host, port = self.decode_payload_content("Q16sH")
- if host[0:12] == '\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xFF\xFF':
- host = socket.inet_ntop(socket.AF_INET, str(host[12:16]))
- elif host[0:6] == '\xfd\x87\xd8\x7e\xeb\x43':
+ if host[0:12] == b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xFF\xFF':
+ host = socket.inet_ntop(socket.AF_INET, _hoststr(host[12:16]))
+ elif host[0:6] == b'\xfd\x87\xd8\x7e\xeb\x43':
# Onion, based on BMD/bitcoind
- host = base64.b32encode(host[6:]).lower() + ".onion"
+ host = base64.b32encode(host[6:]).lower() + b".onion"
else:
- host = socket.inet_ntop(socket.AF_INET6, str(host))
- if host == "":
+ host = socket.inet_ntop(socket.AF_INET6, _hoststr(host))
+ if host == b"":
# This can happen on Windows systems which are not 64-bit
# compatible so let us drop the IPv6 address.
- host = socket.inet_ntop(socket.AF_INET, str(host[12:16]))
+ host = socket.inet_ntop(socket.AF_INET, _hoststr(host[12:16]))
return Node(services, host, port)
@@ -334,7 +348,7 @@ class BMProto(AdvancedDispatcher, ObjectTracker):
if now < self.skipUntil:
return True
for i in items:
- self.pendingUpload[str(i)] = now
+ self.pendingUpload[i] = now
return True
def _command_inv(self, extend_dandelion_stem=False):
@@ -353,7 +367,7 @@ class BMProto(AdvancedDispatcher, ObjectTracker):
if extend_dandelion_stem and not dandelion_ins.enabled:
return True
- for i in map(str, items):
+ for i in items:
if i in state.Inventory and not dandelion_ins.hasHash(i):
continue
if extend_dandelion_stem and not dandelion_ins.hasHash(i):
@@ -409,13 +423,17 @@ class BMProto(AdvancedDispatcher, ObjectTracker):
try:
self.object.checkObjectByType()
+ if six.PY2:
+ data_buffer = buffer(self.object.data)
+ else: # assume six.PY3
+ data_buffer = memoryview(self.object.data)
objectProcessorQueue.put((
- self.object.objectType, buffer(self.object.data))) # noqa: F821
+ self.object.objectType, data_buffer)) # noqa: F821
except BMObjectInvalidError:
BMProto.stopDownloadingObject(self.object.inventoryHash, True)
else:
try:
- del missingObjects[self.object.inventoryHash]
+ del missingObjects[bytes(self.object.inventoryHash)]
except KeyError:
pass
@@ -424,10 +442,16 @@ class BMProto(AdvancedDispatcher, ObjectTracker):
dandelion_ins.removeHash(
self.object.inventoryHash, "cycle detection")
+ if six.PY2:
+ object_buffer = buffer(self.payload[objectOffset:])
+ tag_buffer = buffer(self.object.tag)
+ else: # assume six.PY3
+ object_buffer = memoryview(self.payload[objectOffset:])
+ tag_buffer = memoryview(self.object.tag)
state.Inventory[self.object.inventoryHash] = (
self.object.objectType, self.object.streamNumber,
- buffer(self.payload[objectOffset:]), self.object.expiresTime, # noqa: F821
- buffer(self.object.tag) # noqa: F821
+ object_buffer, self.object.expiresTime, # noqa: F821
+ tag_buffer # noqa: F821
)
self.handleReceivedObject(
self.object.streamNumber, self.object.inventoryHash)
@@ -443,11 +467,10 @@ class BMProto(AdvancedDispatcher, ObjectTracker):
"""Incoming addresses, process them"""
# not using services
for seenTime, stream, _, ip, port in self._decode_addr():
- ip = str(ip)
if (
- stream not in connectionpool.pool.streams
+ stream not in network.connectionpool.pool.streams
# FIXME: should check against complete list
- or ip.startswith('bootstrap')
+ or ip.decode("utf-8", "replace").startswith('bootstrap')
):
continue
decodedIP = protocol.checkIPAddress(ip)
@@ -477,7 +500,7 @@ class BMProto(AdvancedDispatcher, ObjectTracker):
def bm_command_ping(self):
"""Incoming ping, respond to it."""
- self.append_write_buf(protocol.CreatePacket('pong'))
+ self.append_write_buf(protocol.CreatePacket(b'pong'))
return True
@staticmethod
@@ -526,21 +549,21 @@ class BMProto(AdvancedDispatcher, ObjectTracker):
logger.debug(
'remote node incoming address: %s:%i',
self.destination.host, self.peerNode.port)
- logger.debug('user agent: %s', self.userAgent)
+ logger.debug('user agent: %s', self.userAgent.decode("utf-8", "replace"))
logger.debug('streams: [%s]', ','.join(map(str, self.streams)))
if not self.peerValidityChecks():
# ABORT afterwards
return True
- self.append_write_buf(protocol.CreatePacket('verack'))
+ self.append_write_buf(protocol.CreatePacket(b'verack'))
self.verackSent = True
ua_valid = re.match(
- r'^/[a-zA-Z]+:[0-9]+\.?[\w\s\(\)\./:;-]*/$', self.userAgent)
+ r'^/[a-zA-Z]+:[0-9]+\.?[\w\s\(\)\./:;-]*/$', _restr(self.userAgent))
if not ua_valid:
- self.userAgent = '/INVALID:0/'
+ self.userAgent = b'/INVALID:0/'
if not self.isOutbound:
self.append_write_buf(protocol.assembleVersionMessage(
self.destination.host, self.destination.port,
- connectionpool.pool.streams, dandelion_ins.enabled, True,
+ network.connectionpool.pool.streams, dandelion_ins.enabled, True,
nodeid=self.nodeid))
logger.debug(
'%(host)s:%(port)i sending version',
@@ -596,7 +619,7 @@ class BMProto(AdvancedDispatcher, ObjectTracker):
'Closed connection to %s because there is no overlapping'
' interest in streams.', self.destination)
return False
- if connectionpool.pool.inboundConnections.get(
+ if network.connectionpool.pool.inboundConnections.get(
self.destination):
try:
if not protocol.checkSocksIP(self.destination.host):
@@ -614,8 +637,8 @@ class BMProto(AdvancedDispatcher, ObjectTracker):
# or server full report the same error to counter deanonymisation
if (
Peer(self.destination.host, self.peerNode.port)
- in connectionpool.pool.inboundConnections
- or len(connectionpool.pool)
+ in network.connectionpool.pool.inboundConnections
+ or len(network.connectionpool.pool)
> config.safeGetInt(
'bitmessagesettings', 'maxtotalconnections')
+ config.safeGetInt(
@@ -627,7 +650,7 @@ class BMProto(AdvancedDispatcher, ObjectTracker):
'Closed connection to %s due to server full'
' or duplicate inbound/outbound.', self.destination)
return False
- if connectionpool.pool.isAlreadyConnected(self.nonce):
+ if network.connectionpool.pool.isAlreadyConnected(self.nonce):
self.append_write_buf(protocol.assembleErrorMessage(
errorText="I'm connected to myself. Closing connection.",
fatal=2))
@@ -641,7 +664,7 @@ class BMProto(AdvancedDispatcher, ObjectTracker):
@staticmethod
def stopDownloadingObject(hashId, forwardAnyway=False):
"""Stop downloading object *hashId*"""
- for connection in connectionpool.pool.connections():
+ for connection in network.connectionpool.pool.connections():
try:
del connection.objectsNewToMe[hashId]
except KeyError:
@@ -653,7 +676,7 @@ class BMProto(AdvancedDispatcher, ObjectTracker):
except KeyError:
pass
try:
- del missingObjects[hashId]
+ del missingObjects[bytes(hashId)]
except KeyError:
pass
diff --git a/src/network/connectionchooser.py b/src/network/connectionchooser.py
index e2981d51..9888aabf 100644
--- a/src/network/connectionchooser.py
+++ b/src/network/connectionchooser.py
@@ -7,7 +7,7 @@ import random
from six.moves import queue
-import knownnodes
+from network import knownnodes
import protocol
import state
@@ -17,10 +17,16 @@ from network import portCheckerQueue
logger = logging.getLogger('default')
+def _ends_with(s, tail):
+ try:
+ return s.endswith(tail)
+ except:
+ return s.decode("utf-8", "replace").endswith(tail)
+
def getDiscoveredPeer():
"""Get a peer from the local peer discovery list"""
try:
- peer = random.choice(state.discoveredPeers.keys()) # nosec B311
+ peer = random.choice(list(state.discoveredPeers.keys())) # nosec B311
except (IndexError, KeyError):
raise ValueError
try:
@@ -48,7 +54,7 @@ def chooseConnection(stream):
return getDiscoveredPeer()
for _ in range(50):
peer = random.choice( # nosec B311
- knownnodes.knownNodes[stream].keys())
+ list(knownnodes.knownNodes[stream].keys()))
try:
peer_info = knownnodes.knownNodes[stream][peer]
if peer_info.get('self'):
@@ -60,10 +66,10 @@ def chooseConnection(stream):
if haveOnion:
# do not connect to raw IP addresses
# --keep all traffic within Tor overlay
- if onionOnly and not peer.host.endswith('.onion'):
+ if onionOnly and not _ends_with(peer.host, '.onion'):
continue
# onion addresses have a higher priority when SOCKS
- if peer.host.endswith('.onion') and rating > 0:
+ if _ends_with(peer.host, '.onion') and rating > 0:
rating = 1
# TODO: need better check
elif not peer.host.startswith('bootstrap'):
diff --git a/src/network/connectionpool.py b/src/network/connectionpool.py
index 519b7b67..62e12884 100644
--- a/src/network/connectionpool.py
+++ b/src/network/connectionpool.py
@@ -9,22 +9,28 @@ import sys
import time
import random
-import asyncore_pollchoose as asyncore
-import knownnodes
+from network import asyncore_pollchoose as asyncore
+from network import knownnodes
import protocol
import state
from bmconfigparser import config
-from connectionchooser import chooseConnection
-from node import Peer
-from proxy import Proxy
-from tcp import (
+from .connectionchooser import chooseConnection
+from .node import Peer
+from .proxy import Proxy
+from .tcp import (
bootstrap, Socks4aBMConnection, Socks5BMConnection,
TCPConnection, TCPServer)
-from udp import UDPSocket
+from .udp import UDPSocket
logger = logging.getLogger('default')
+def _ends_with(s, tail):
+ try:
+ return s.endswith(tail)
+ except:
+ return s.decode("utf-8", "replace").endswith(tail)
+
class BMConnectionPool(object):
"""Pool of all existing connections"""
# pylint: disable=too-many-instance-attributes
@@ -78,7 +84,7 @@ class BMConnectionPool(object):
Shortcut for combined list of connections from
`inboundConnections` and `outboundConnections` dicts
"""
- return self.inboundConnections.values() + self.outboundConnections.values()
+ return list(self.inboundConnections.values()) + list(self.outboundConnections.values())
def establishedConnections(self):
"""Shortcut for list of connections having fullyEstablished == True"""
@@ -160,8 +166,8 @@ class BMConnectionPool(object):
@staticmethod
def getListeningIP():
"""What IP are we supposed to be listening on?"""
- if config.safeGet(
- "bitmessagesettings", "onionhostname", "").endswith(".onion"):
+ if _ends_with(config.safeGet(
+ "bitmessagesettings", "onionhostname", ""), ".onion"):
host = config.safeGet(
"bitmessagesettings", "onionbindip")
else:
@@ -314,7 +320,7 @@ class BMConnectionPool(object):
continue
try:
- if chosen.host.endswith(".onion") and Proxy.onion_proxy:
+ if _ends_with(chosen.host, ".onion") and Proxy.onion_proxy:
if onionsocksproxytype == "SOCKS5":
self.addConnection(Socks5BMConnection(chosen))
elif onionsocksproxytype == "SOCKS4a":
@@ -381,14 +387,14 @@ class BMConnectionPool(object):
minTx -= 300 - 20
if i.lastTx < minTx:
if i.fullyEstablished:
- i.append_write_buf(protocol.CreatePacket('ping'))
+ i.append_write_buf(protocol.CreatePacket(b'ping'))
else:
i.close_reason = "Timeout (%is)" % (
time.time() - i.lastTx)
i.set_state("close")
for i in (
self.connections()
- + self.listeningSockets.values() + self.udpSockets.values()
+ + list(self.listeningSockets.values()) + list(self.udpSockets.values())
):
if not (i.accepting or i.connecting or i.connected):
reaper.append(i)
diff --git a/src/network/dandelion.py b/src/network/dandelion.py
index 564a35f9..ce87653c 100644
--- a/src/network/dandelion.py
+++ b/src/network/dandelion.py
@@ -6,6 +6,8 @@ from collections import namedtuple
from random import choice, expovariate, sample
from threading import RLock
from time import time
+import six
+from binascii import hexlify
# randomise routes after 600 seconds
@@ -64,7 +66,7 @@ class Dandelion: # pylint: disable=old-style-class
"""Add inventory vector to dandelion stem return status of dandelion enabled"""
assert self.enabled is not None
with self.lock:
- self.hashMap[hashId] = Stem(
+ self.hashMap[bytes(hashId)] = Stem(
self.getNodeStem(source),
stream,
self.poissonTimeout())
@@ -75,9 +77,10 @@ class Dandelion: # pylint: disable=old-style-class
include streams, we only learn this after receiving the object)
"""
with self.lock:
- if hashId in self.hashMap:
- self.hashMap[hashId] = Stem(
- self.hashMap[hashId].child,
+ hashId_bytes = bytes(hashId)
+ if hashId_bytes in self.hashMap:
+ self.hashMap[hashId_bytes] = Stem(
+ self.hashMap[hashId_bytes].child,
stream,
self.poissonTimeout())
@@ -86,20 +89,20 @@ class Dandelion: # pylint: disable=old-style-class
if logger.isEnabledFor(logging.DEBUG):
logger.debug(
'%s entering fluff mode due to %s.',
- ''.join('%02x' % ord(i) for i in hashId), reason)
+ hexlify(hashId), reason)
with self.lock:
try:
- del self.hashMap[hashId]
+ del self.hashMap[bytes(hashId)]
except KeyError:
pass
def hasHash(self, hashId):
"""Is inventory vector in stem mode?"""
- return hashId in self.hashMap
+ return bytes(hashId) in self.hashMap
def objectChildStem(self, hashId):
"""Child (i.e. next) node for an inventory vector during stem mode"""
- return self.hashMap[hashId].child
+ return self.hashMap[bytes(hashId)].child
def maybeAddStem(self, connection, invQueue):
"""
@@ -111,12 +114,12 @@ class Dandelion: # pylint: disable=old-style-class
with self.lock:
if len(self.stem) < MAX_STEMS:
self.stem.append(connection)
- for k in (k for k, v in self.nodeMap.iteritems() if v is None):
+ for k in (k for k, v in six.iteritems(self.nodeMap) if v is None):
self.nodeMap[k] = connection
- for k, v in {
- k: v for k, v in self.hashMap.iteritems()
+ for k, v in six.iteritems({
+ k: v for k, v in six.iteritems(self.hashMap)
if v.child is None
- }.iteritems():
+ }):
self.hashMap[k] = Stem(
connection, v.stream, self.poissonTimeout())
invQueue.put((v.stream, k, v.child))
@@ -132,14 +135,14 @@ class Dandelion: # pylint: disable=old-style-class
self.stem.remove(connection)
# active mappings to pointing to the removed node
for k in (
- k for k, v in self.nodeMap.iteritems()
+ k for k, v in six.iteritems(self.nodeMap)
if v == connection
):
self.nodeMap[k] = None
- for k, v in {
- k: v for k, v in self.hashMap.iteritems()
+ for k, v in six.iteritems({
+ k: v for k, v in six.iteritems(self.hashMap)
if v.child == connection
- }.iteritems():
+ }):
self.hashMap[k] = Stem(
None, v.stream, self.poissonTimeout())
@@ -180,7 +183,7 @@ class Dandelion: # pylint: disable=old-style-class
with self.lock:
deadline = time()
toDelete = [
- [v.stream, k, v.child] for k, v in self.hashMap.iteritems()
+ [v.stream, k, v.child] for k, v in six.iteritems(self.hashMap)
if v.timeout < deadline
]
@@ -199,10 +202,10 @@ class Dandelion: # pylint: disable=old-style-class
try:
# random two connections
self.stem = sample(
- self.pool.outboundConnections.values(), MAX_STEMS)
+ sorted(self.pool.outboundConnections.values()), MAX_STEMS)
# not enough stems available
except ValueError:
- self.stem = self.pool.outboundConnections.values()
+ self.stem = list(self.pool.outboundConnections.values())
self.nodeMap = {}
# hashMap stays to cater for pending stems
self.refresh = time() + REASSIGN_INTERVAL
diff --git a/src/network/downloadthread.py b/src/network/downloadthread.py
index 7c8bccb6..74e1afc8 100644
--- a/src/network/downloadthread.py
+++ b/src/network/downloadthread.py
@@ -4,12 +4,13 @@
import time
import random
import state
+import six
import addresses
import protocol
-import connectionpool
+from network import connectionpool
from network import dandelion_ins
-from objectracker import missingObjects
-from threads import StoppableThread
+from .objectracker import missingObjects
+from .threads import StoppableThread
class DownloadThread(StoppableThread):
@@ -29,7 +30,7 @@ class DownloadThread(StoppableThread):
deadline = time.time() - self.requestExpires
try:
toDelete = [
- k for k, v in missingObjects.iteritems()
+ k for k, v in six.iteritems(missingObjects)
if v < deadline]
except RuntimeError:
pass
@@ -68,11 +69,11 @@ class DownloadThread(StoppableThread):
continue
payload.extend(chunk)
chunkCount += 1
- missingObjects[chunk] = now
+ missingObjects[bytes(chunk)] = now
if not chunkCount:
continue
payload[0:0] = addresses.encodeVarint(chunkCount)
- i.append_write_buf(protocol.CreatePacket('getdata', payload))
+ i.append_write_buf(protocol.CreatePacket(b'getdata', payload))
self.logger.debug(
'%s:%i Requesting %i objects',
i.destination.host, i.destination.port, chunkCount)
diff --git a/src/network/http.py b/src/network/http.py
index d7a938fa..af0015c7 100644
--- a/src/network/http.py
+++ b/src/network/http.py
@@ -1,10 +1,10 @@
import socket
-from advanceddispatcher import AdvancedDispatcher
-import asyncore_pollchoose as asyncore
-from proxy import ProxyError
-from socks5 import Socks5Connection, Socks5Resolver
-from socks4a import Socks4aConnection, Socks4aResolver
+from .advanceddispatcher import AdvancedDispatcher
+from network import asyncore_pollchoose as asyncore
+from .proxy import ProxyError
+from .socks5 import Socks5Connection, Socks5Resolver
+from .socks4a import Socks4aConnection, Socks4aResolver
class HttpError(ProxyError):
diff --git a/src/network/invthread.py b/src/network/invthread.py
index 503eefa1..e04ccc5b 100644
--- a/src/network/invthread.py
+++ b/src/network/invthread.py
@@ -1,16 +1,16 @@
"""
Thread to send inv annoucements
"""
-import Queue
+from six.moves import queue as Queue
import random
from time import time
import addresses
import protocol
import state
-import connectionpool
+from network import connectionpool
from network import dandelion_ins, invQueue
-from threads import StoppableThread
+from .threads import StoppableThread
def handleExpiredDandelion(expired):
@@ -90,15 +90,15 @@ class InvThread(StoppableThread):
if fluffs:
random.shuffle(fluffs)
connection.append_write_buf(protocol.CreatePacket(
- 'inv',
+ b'inv',
addresses.encodeVarint(
- len(fluffs)) + ''.join(fluffs)))
+ len(fluffs)) + b''.join(fluffs)))
if stems:
random.shuffle(stems)
connection.append_write_buf(protocol.CreatePacket(
- 'dinv',
+ b'dinv',
addresses.encodeVarint(
- len(stems)) + ''.join(stems)))
+ len(stems)) + b''.join(stems)))
invQueue.iterate()
for _ in range(len(chunk)):
diff --git a/src/network/knownnodes.py b/src/network/knownnodes.py
index c53be2cd..2ce698f9 100644
--- a/src/network/knownnodes.py
+++ b/src/network/knownnodes.py
@@ -10,10 +10,8 @@ import os
import pickle # nosec B403
import threading
import time
-try:
- from collections.abc import Iterable
-except ImportError:
- from collections import Iterable
+from six.moves.collections_abc import Iterable
+import six
import state
from bmconfigparser import config
@@ -54,8 +52,8 @@ def json_serialize_knownnodes(output):
Reorganize knownnodes dict and write it as JSON to output
"""
_serialized = []
- for stream, peers in knownNodes.iteritems():
- for peer, info in peers.iteritems():
+ for stream, peers in six.iteritems(knownNodes):
+ for peer, info in six.iteritems(peers):
info.update(rating=round(info.get('rating', 0), 2))
_serialized.append({
'stream': stream, 'peer': peer._asdict(), 'info': info
@@ -87,7 +85,7 @@ def pickle_deserialize_old_knownnodes(source):
global knownNodes
knownNodes = pickle.load(source) # nosec B301
for stream in knownNodes.keys():
- for node, params in knownNodes[stream].iteritems():
+ for node, params in six.iteritems(knownNodes[stream]):
if isinstance(params, (float, int)):
addKnownNode(stream, node, params)
@@ -97,7 +95,7 @@ def saveKnownNodes(dirName=None):
if dirName is None:
dirName = state.appdata
with knownNodesLock:
- with open(os.path.join(dirName, 'knownnodes.dat'), 'wb') as output:
+ with open(os.path.join(dirName, 'knownnodes.dat'), 'w') as output:
json_serialize_knownnodes(output)
@@ -108,6 +106,12 @@ def addKnownNode(stream, peer, lastseen=None, is_self=False):
Returns True if added a new node.
"""
# pylint: disable=too-many-branches
+ if not isinstance(peer.host, str):
+ try:
+ peer = Peer(peer.host.decode("ascii"), peer.port)
+ except UnicodeDecodeError as err:
+ logger.warning("Invalid host: {}".format(peer.host.decode("ascii", "backslashreplace")))
+ return
if isinstance(stream, Iterable):
with knownNodesLock:
for s in stream:
@@ -153,7 +157,7 @@ def createDefaultKnownNodes():
def readKnownNodes():
"""Load knownnodes from filesystem"""
try:
- with open(state.appdata + 'knownnodes.dat', 'rb') as source:
+ with open(state.appdata + 'knownnodes.dat', 'r') as source:
with knownNodesLock:
try:
json_deserialize_knownnodes(source)
diff --git a/src/network/networkthread.py b/src/network/networkthread.py
index 640d47a1..6a4be2a4 100644
--- a/src/network/networkthread.py
+++ b/src/network/networkthread.py
@@ -2,9 +2,9 @@
A thread to handle network concerns
"""
import network.asyncore_pollchoose as asyncore
-import connectionpool
+from network import connectionpool
from queues import excQueue
-from threads import StoppableThread
+from .threads import StoppableThread
class BMNetworkThread(StoppableThread):
diff --git a/src/network/objectracker.py b/src/network/objectracker.py
index 91bb0552..b14452ea 100644
--- a/src/network/objectracker.py
+++ b/src/network/objectracker.py
@@ -3,8 +3,9 @@ Module for tracking objects
"""
import time
from threading import RLock
+import six
-import connectionpool
+import network.connectionpool # use long name to address recursive import
from network import dandelion_ins
from randomtrackingdict import RandomTrackingDict
@@ -75,32 +76,35 @@ class ObjectTracker(object):
with self.objectsNewToThemLock:
self.objectsNewToThem = {
k: v
- for k, v in self.objectsNewToThem.iteritems()
+ for k, v in six.iteritems(self.objectsNewToThem)
if v >= deadline}
self.lastCleaned = time.time()
def hasObj(self, hashid):
"""Do we already have object?"""
+ hashid_bytes = bytes(hashid)
if haveBloom:
- return hashid in self.invBloom
- return hashid in self.objectsNewToMe
+ return hashid_bytes in self.invBloom
+ return hashid_bytes in self.objectsNewToMe
def handleReceivedInventory(self, hashId):
"""Handling received inventory"""
+ hashId_bytes = bytes(hashId)
if haveBloom:
- self.invBloom.add(hashId)
+ self.invBloom.add(hashId_bytes)
try:
with self.objectsNewToThemLock:
- del self.objectsNewToThem[hashId]
+ del self.objectsNewToThem[hashId_bytes]
except KeyError:
pass
- if hashId not in missingObjects:
- missingObjects[hashId] = time.time()
+ if hashId_bytes not in missingObjects:
+ missingObjects[hashId_bytes] = time.time()
self.objectsNewToMe[hashId] = True
def handleReceivedObject(self, streamNumber, hashid):
"""Handling received object"""
- for i in connectionpool.pool.connections():
+ hashid_bytes = bytes(hashid)
+ for i in network.connectionpool.pool.connections():
if not i.fullyEstablished:
continue
try:
@@ -110,7 +114,7 @@ class ObjectTracker(object):
not dandelion_ins.hasHash(hashid)
or dandelion_ins.objectChildStem(hashid) == i):
with i.objectsNewToThemLock:
- i.objectsNewToThem[hashid] = time.time()
+ i.objectsNewToThem[hashid_bytes] = time.time()
# update stream number,
# which we didn't have when we just received the dinv
# also resets expiration of the stem mode
@@ -119,7 +123,7 @@ class ObjectTracker(object):
if i == self:
try:
with i.objectsNewToThemLock:
- del i.objectsNewToThem[hashid]
+ del i.objectsNewToThem[hashid_bytes]
except KeyError:
pass
self.objectsNewToMe.setLastObject()
@@ -133,4 +137,4 @@ class ObjectTracker(object):
def addAddr(self, hashid):
"""WIP, should be moved to addrthread.py or removed"""
if haveBloom:
- self.addrBloom.add(hashid)
+ self.addrBloom.add(bytes(hashid))
diff --git a/src/network/proxy.py b/src/network/proxy.py
index ed1af127..9cce6bf6 100644
--- a/src/network/proxy.py
+++ b/src/network/proxy.py
@@ -6,14 +6,20 @@ import logging
import socket
import time
-import asyncore_pollchoose as asyncore
-from advanceddispatcher import AdvancedDispatcher
+from network import asyncore_pollchoose as asyncore
+from .advanceddispatcher import AdvancedDispatcher
from bmconfigparser import config
-from node import Peer
+from .node import Peer
logger = logging.getLogger('default')
+def _ends_with(s, tail):
+ try:
+ return s.endswith(tail)
+ except:
+ return s.decode("utf-8", "replace").endswith(tail)
+
class ProxyError(Exception):
"""Base proxy exception class"""
errorCodes = ("Unknown error",)
@@ -125,7 +131,7 @@ class Proxy(AdvancedDispatcher):
self.auth = None
self.connect(
self.onion_proxy
- if address.host.endswith(".onion") and self.onion_proxy else
+ if _ends_with(address.host, ".onion") and self.onion_proxy else
self.proxy
)
diff --git a/src/network/receivequeuethread.py b/src/network/receivequeuethread.py
index 88d3b740..d7ba24d0 100644
--- a/src/network/receivequeuethread.py
+++ b/src/network/receivequeuethread.py
@@ -2,13 +2,13 @@
Process data incoming from network
"""
import errno
-import Queue
+from six.moves import queue as Queue
import socket
-import connectionpool
+from network import connectionpool
from network.advanceddispatcher import UnknownStateError
from network import receiveDataQueue
-from threads import StoppableThread
+from .threads import StoppableThread
class ReceiveQueueThread(StoppableThread):
diff --git a/src/network/socks4a.py b/src/network/socks4a.py
index e9786168..122114b3 100644
--- a/src/network/socks4a.py
+++ b/src/network/socks4a.py
@@ -5,8 +5,9 @@ SOCKS4a proxy module
import logging
import socket
import struct
+import six
-from proxy import GeneralProxyError, Proxy, ProxyError
+from .proxy import GeneralProxyError, Proxy, ProxyError
logger = logging.getLogger('default')
@@ -39,16 +40,16 @@ class Socks4a(Proxy):
def state_pre_connect(self):
"""Handle feedback from SOCKS4a while it is connecting on our behalf"""
# Get the response
- if self.read_buf[0:1] != chr(0x00).encode():
+ if self.read_buf[0:1] != six.int2byte(0x00):
# bad data
self.close()
raise GeneralProxyError(1)
- elif self.read_buf[1:2] != chr(0x5A).encode():
+ elif self.read_buf[1:2] != six.int2byte(0x5A):
# Connection failed
self.close()
- if ord(self.read_buf[1:2]) in (91, 92, 93):
+ if six.byte2int(self.read_buf[1:2]) in (91, 92, 93):
# socks 4 error
- raise Socks4aError(ord(self.read_buf[1:2]) - 90)
+ raise Socks4aError(six.byte2int(self.read_buf[1:2]) - 90)
else:
raise Socks4aError(4)
# Get the bound address/port
@@ -102,9 +103,9 @@ class Socks4aConnection(Socks4a):
self.append_write_buf(self.ipaddr)
if self._auth:
self.append_write_buf(self._auth[0])
- self.append_write_buf(chr(0x00).encode())
+ self.append_write_buf(six.int2byte(0x00))
if rmtrslv:
- self.append_write_buf(self.destination[0] + chr(0x00).encode())
+ self.append_write_buf(self.destination[0].encode("utf-8", "replace") + six.int2byte(0x00))
self.set_state("pre_connect", length=0, expectBytes=8)
return True
@@ -132,8 +133,8 @@ class Socks4aResolver(Socks4a):
self.append_write_buf(struct.pack("BBBB", 0x00, 0x00, 0x00, 0x01))
if self._auth:
self.append_write_buf(self._auth[0])
- self.append_write_buf(chr(0x00).encode())
- self.append_write_buf(self.host + chr(0x00).encode())
+ self.append_write_buf(six.int2byte(0x00))
+ self.append_write_buf(self.host + six.int2byte(0x00))
self.set_state("pre_connect", length=0, expectBytes=8)
return True
diff --git a/src/network/socks5.py b/src/network/socks5.py
index d1daae42..ca5a52e8 100644
--- a/src/network/socks5.py
+++ b/src/network/socks5.py
@@ -6,9 +6,10 @@ SOCKS5 proxy module
import logging
import socket
import struct
+import six
-from node import Peer
-from proxy import GeneralProxyError, Proxy, ProxyError
+from .node import Peer
+from .proxy import GeneralProxyError, Proxy, ProxyError
logger = logging.getLogger('default')
@@ -97,20 +98,20 @@ class Socks5(Proxy):
def state_pre_connect(self):
"""Handle feedback from socks5 while it is connecting on our behalf."""
# Get the response
- if self.read_buf[0:1] != chr(0x05).encode():
+ if self.read_buf[0:1] != six.int2byte(0x05):
self.close()
raise GeneralProxyError(1)
- elif self.read_buf[1:2] != chr(0x00).encode():
+ elif self.read_buf[1:2] != six.int2byte(0x00):
# Connection failed
self.close()
- if ord(self.read_buf[1:2]) <= 8:
- raise Socks5Error(ord(self.read_buf[1:2]))
+ if six.byte2int(self.read_buf[1:2]) <= 8:
+ raise Socks5Error(six.byte2int(self.read_buf[1:2]))
else:
raise Socks5Error(9)
# Get the bound address/port
- elif self.read_buf[3:4] == chr(0x01).encode():
+ elif self.read_buf[3:4] == six.int2byte(0x01):
self.set_state("proxy_addr_1", length=4, expectBytes=4)
- elif self.read_buf[3:4] == chr(0x03).encode():
+ elif self.read_buf[3:4] == six.int2byte(0x03):
self.set_state("proxy_addr_2_1", length=4, expectBytes=1)
else:
self.close()
@@ -129,7 +130,7 @@ class Socks5(Proxy):
(e.g. IPv6, onion, ...). This is part 1 which retrieves the
length of the data.
"""
- self.address_length = ord(self.read_buf[0:1])
+ self.address_length = six.byte2int(self.read_buf[0:1])
self.set_state(
"proxy_addr_2_2", length=1, expectBytes=self.address_length)
return True
@@ -171,19 +172,19 @@ class Socks5Connection(Socks5):
# use the IPv4 address request even if remote resolving was specified.
try:
self.ipaddr = socket.inet_aton(self.destination[0])
- self.append_write_buf(chr(0x01).encode() + self.ipaddr)
+ self.append_write_buf(six.int2byte(0x01) + self.ipaddr)
except socket.error: # may be IPv6!
# Well it's not an IP number, so it's probably a DNS name.
if self._remote_dns:
# Resolve remotely
self.ipaddr = None
- self.append_write_buf(chr(0x03).encode() + chr(
- len(self.destination[0])).encode() + self.destination[0])
+ self.append_write_buf(six.int2byte(0x03) + six.int2byte(
+ len(self.destination[0])) + self.destination[0].encode("utf-8", "replace"))
else:
# Resolve locally
self.ipaddr = socket.inet_aton(
socket.gethostbyname(self.destination[0]))
- self.append_write_buf(chr(0x01).encode() + self.ipaddr)
+ self.append_write_buf(six.int2byte(0x01) + self.ipaddr)
self.append_write_buf(struct.pack(">H", self.destination[1]))
self.set_state("pre_connect", length=0, expectBytes=4)
return True
@@ -208,8 +209,8 @@ class Socks5Resolver(Socks5):
"""Perform resolving"""
# Now we can request the actual connection
self.append_write_buf(struct.pack('BBB', 0x05, 0xF0, 0x00))
- self.append_write_buf(chr(0x03).encode() + chr(
- len(self.host)).encode() + str(self.host))
+ self.append_write_buf(six.int2byte(0x03) + six.int2byte(
+ len(self.host)) + bytes(self.host))
self.append_write_buf(struct.pack(">H", self.port))
self.set_state("pre_connect", length=0, expectBytes=4)
return True
diff --git a/src/network/stats.py b/src/network/stats.py
index 0ab1ae0f..ea2c40c9 100644
--- a/src/network/stats.py
+++ b/src/network/stats.py
@@ -3,9 +3,9 @@ Network statistics
"""
import time
-import asyncore_pollchoose as asyncore
-import connectionpool
-from objectracker import missingObjects
+from network import asyncore_pollchoose as asyncore
+from network import connectionpool
+from .objectracker import missingObjects
lastReceivedTimestamp = time.time()
diff --git a/src/network/tcp.py b/src/network/tcp.py
index db7d6595..e7671197 100644
--- a/src/network/tcp.py
+++ b/src/network/tcp.py
@@ -8,28 +8,29 @@ import math
import random
import socket
import time
+import six
# magic imports!
import addresses
import l10n
import protocol
import state
-import connectionpool
+import network.connectionpool # use long name to address recursive import
from bmconfigparser import config
from highlevelcrypto import randomBytes
from network import dandelion_ins, invQueue, receiveDataQueue
from queues import UISignalQueue
from tr import _translate
-import asyncore_pollchoose as asyncore
-import knownnodes
+from network import asyncore_pollchoose as asyncore
+from network import knownnodes
from network.advanceddispatcher import AdvancedDispatcher
from network.bmproto import BMProto
from network.objectracker import ObjectTracker
from network.socks4a import Socks4aConnection
from network.socks5 import Socks5Connection
from network.tls import TLSDispatcher
-from node import Peer
+from .node import Peer
logger = logging.getLogger('default')
@@ -39,6 +40,12 @@ maximumAgeOfNodesThatIAdvertiseToOthers = 10800 #: Equals three hours
maximumTimeOffsetWrongCount = 3 #: Connections with wrong time offset
+def _ends_with(s, tail):
+ try:
+ return s.endswith(tail)
+ except:
+ return s.decode("utf-8", "replace").endswith(tail)
+
class TCPConnection(BMProto, TLSDispatcher):
# pylint: disable=too-many-instance-attributes
"""
@@ -138,9 +145,9 @@ class TCPConnection(BMProto, TLSDispatcher):
'updateStatusBar',
_translate(
"MainWindow",
- "The time on your computer, %1, may be wrong. "
+ "The time on your computer, {0}, may be wrong. "
"Please verify your settings."
- ).arg(l10n.formatTimestamp())))
+ ).format(l10n.formatTimestamp())))
def state_connection_fully_established(self):
"""
@@ -191,10 +198,10 @@ class TCPConnection(BMProto, TLSDispatcher):
# only if more recent than 3 hours
# and having positive or neutral rating
filtered = [
- (k, v) for k, v in nodes.iteritems()
+ (k, v) for k, v in six.iteritems(nodes)
if v["lastseen"] > int(time.time())
- maximumAgeOfNodesThatIAdvertiseToOthers
- and v["rating"] >= 0 and not k.host.endswith('.onion')
+ and v["rating"] >= 0 and not _ends_with(k.host, '.onion')
]
# sent 250 only if the remote isn't interested in it
elemCount = min(
@@ -220,7 +227,7 @@ class TCPConnection(BMProto, TLSDispatcher):
'Sending huge inv message with %i objects to just this'
' one peer', objectCount)
self.append_write_buf(protocol.CreatePacket(
- 'inv', addresses.encodeVarint(objectCount) + payload))
+ b'inv', addresses.encodeVarint(objectCount) + payload))
# Select all hashes for objects in this stream.
bigInvList = {}
@@ -267,7 +274,7 @@ class TCPConnection(BMProto, TLSDispatcher):
self.append_write_buf(
protocol.assembleVersionMessage(
self.destination.host, self.destination.port,
- connectionpool.pool.streams, dandelion_ins.enabled,
+ network.connectionpool.pool.streams, dandelion_ins.enabled,
False, nodeid=self.nodeid))
self.connectedAt = time.time()
receiveDataQueue.put(self.destination)
@@ -318,7 +325,7 @@ class Socks5BMConnection(Socks5Connection, TCPConnection):
self.append_write_buf(
protocol.assembleVersionMessage(
self.destination.host, self.destination.port,
- connectionpool.pool.streams, dandelion_ins.enabled,
+ network.connectionpool.pool.streams, dandelion_ins.enabled,
False, nodeid=self.nodeid))
self.set_state("bm_header", expectBytes=protocol.Header.size)
return True
@@ -342,7 +349,7 @@ class Socks4aBMConnection(Socks4aConnection, TCPConnection):
self.append_write_buf(
protocol.assembleVersionMessage(
self.destination.host, self.destination.port,
- connectionpool.pool.streams, dandelion_ins.enabled,
+ network.connectionpool.pool.streams, dandelion_ins.enabled,
False, nodeid=self.nodeid))
self.set_state("bm_header", expectBytes=protocol.Header.size)
return True
@@ -430,7 +437,7 @@ class TCPServer(AdvancedDispatcher):
state.ownAddresses[Peer(*sock.getsockname())] = True
if (
- len(connectionpool.pool)
+ len(network.connectionpool.pool)
> config.safeGetInt(
'bitmessagesettings', 'maxtotalconnections')
+ config.safeGetInt(
@@ -442,7 +449,7 @@ class TCPServer(AdvancedDispatcher):
sock.close()
return
try:
- connectionpool.pool.addConnection(
+ network.connectionpool.pool.addConnection(
TCPConnection(sock=sock))
except socket.error:
pass
diff --git a/src/network/tls.py b/src/network/tls.py
index 2d5d5e1b..05a03906 100644
--- a/src/network/tls.py
+++ b/src/network/tls.py
@@ -6,6 +6,7 @@ import os
import socket
import ssl
import sys
+import six
import network.asyncore_pollchoose as asyncore
import paths
@@ -34,7 +35,7 @@ else:
# ciphers
if (
ssl.OPENSSL_VERSION_NUMBER >= 0x10100000
- and not ssl.OPENSSL_VERSION.startswith(b"LibreSSL")
+ and not ssl.OPENSSL_VERSION.startswith("LibreSSL")
):
sslProtocolCiphers = "AECDH-AES256-SHA@SECLEVEL=0"
else:
@@ -58,25 +59,45 @@ class TLSDispatcher(AdvancedDispatcher):
self.tlsDone = False
self.tlsVersion = "N/A"
self.isSSL = False
+ if six.PY3 or ssl.OPENSSL_VERSION_NUMBER >= 0x30000000:
+ self.tlsPrepared = False
def state_tls_init(self):
"""Prepare sockets for TLS handshake"""
self.isSSL = True
self.tlsStarted = True
+
+ if six.PY3 or ssl.OPENSSL_VERSION_NUMBER >= 0x30000000:
+ self.want_read = self.want_write = True
+ self.set_state("tls_handshake")
+ return False
+
+ return self.do_tls_init()
+
+ def do_tls_init(self):
# Once the connection has been established,
# it's safe to wrap the socket.
if sys.version_info >= (2, 7, 9):
- context = ssl.create_default_context(
- purpose=ssl.Purpose.SERVER_AUTH
- if self.server_side else ssl.Purpose.CLIENT_AUTH)
+ if ssl.OPENSSL_VERSION_NUMBER >= 0x30000000:
+ context = ssl.SSLContext(ssl.PROTOCOL_TLS_SERVER
+ if self.server_side else ssl.PROTOCOL_TLS_CLIENT)
+ else:
+ context = ssl.create_default_context(
+ purpose=ssl.Purpose.SERVER_AUTH
+ if self.server_side else ssl.Purpose.CLIENT_AUTH)
context.set_ciphers(self.ciphers)
context.set_ecdh_curve("secp256k1")
context.check_hostname = False
context.verify_mode = ssl.CERT_NONE
# also exclude TLSv1 and TLSv1.1 in the future
- context.options = ssl.OP_ALL | ssl.OP_NO_SSLv2 |\
- ssl.OP_NO_SSLv3 | ssl.OP_SINGLE_ECDH_USE |\
- ssl.OP_CIPHER_SERVER_PREFERENCE
+ if ssl.OPENSSL_VERSION_NUMBER >= 0x30000000:
+ context.options = ssl.OP_ALL | ssl.OP_NO_SSLv2 |\
+ ssl.OP_NO_SSLv3 | ssl.OP_SINGLE_ECDH_USE |\
+ ssl.OP_CIPHER_SERVER_PREFERENCE | ssl.OP_NO_TLSv1_3
+ else:
+ context.options = ssl.OP_ALL | ssl.OP_NO_SSLv2 |\
+ ssl.OP_NO_SSLv3 | ssl.OP_SINGLE_ECDH_USE |\
+ ssl.OP_CIPHER_SERVER_PREFERENCE
self.sslSocket = context.wrap_socket(
self.socket, server_side=self.server_side,
do_handshake_on_connect=False)
@@ -88,7 +109,10 @@ class TLSDispatcher(AdvancedDispatcher):
ciphers=self.ciphers, do_handshake_on_connect=False)
self.sslSocket.setblocking(0)
self.want_read = self.want_write = True
- self.set_state("tls_handshake")
+ if six.PY3 or ssl.OPENSSL_VERSION_NUMBER >= 0x30000000:
+ self.tlsPrepared = True
+ else:
+ self.set_state("tls_handshake")
return False
@staticmethod
@@ -114,7 +138,9 @@ class TLSDispatcher(AdvancedDispatcher):
# during TLS handshake, and after flushing write buffer,
# return status of last handshake attempt
if self.tlsStarted and not self.tlsDone and not self.write_buf:
- logger.debug('tls readable, %r', self.want_read)
+ # with OpenSSL 3, excessive logs are spitted
+ if ssl.OPENSSL_VERSION_NUMBER < 0x30000000:
+ logger.debug('tls readable, %r', self.want_read)
return self.want_read
# prior to TLS handshake,
# receiveDataThread should emulate synchronous behaviour
@@ -134,6 +160,10 @@ class TLSDispatcher(AdvancedDispatcher):
try:
# wait for write buffer flush
if self.tlsStarted and not self.tlsDone and not self.write_buf:
+ if six.PY3 or ssl.OPENSSL_VERSION_NUMBER >= 0x30000000:
+ if not self.tlsPrepared:
+ self.do_tls_init()
+ return
self.tls_handshake()
else:
AdvancedDispatcher.handle_read(self)
@@ -156,6 +186,10 @@ class TLSDispatcher(AdvancedDispatcher):
try:
# wait for write buffer flush
if self.tlsStarted and not self.tlsDone and not self.write_buf:
+ if six.PY3 or ssl.OPENSSL_VERSION_NUMBER >= 0x30000000:
+ if not self.tlsPrepared:
+ self.do_tls_init()
+ return
self.tls_handshake()
else:
AdvancedDispatcher.handle_write(self)
diff --git a/src/network/udp.py b/src/network/udp.py
index 30643d40..051aa055 100644
--- a/src/network/udp.py
+++ b/src/network/udp.py
@@ -8,12 +8,12 @@ import time
# magic imports!
import protocol
import state
-import connectionpool
+import network.connectionpool # use long name to address recursive import
from network import receiveDataQueue
-from bmproto import BMProto
-from node import Peer
-from objectracker import ObjectTracker
+from .bmproto import BMProto
+from .node import Peer
+from .objectracker import ObjectTracker
logger = logging.getLogger('default')
@@ -81,8 +81,8 @@ class UDPSocket(BMProto): # pylint: disable=too-many-instance-attributes
return True
remoteport = False
for seenTime, stream, _, ip, port in addresses:
- decodedIP = protocol.checkIPAddress(str(ip))
- if stream not in connectionpool.pool.streams:
+ decodedIP = protocol.checkIPAddress(ip)
+ if stream not in network.connectionpool.pool.streams:
continue
if (seenTime < time.time() - protocol.MAX_TIME_OFFSET
or seenTime > time.time() + protocol.MAX_TIME_OFFSET):
diff --git a/src/network/uploadthread.py b/src/network/uploadthread.py
index 60209832..b303a63e 100644
--- a/src/network/uploadthread.py
+++ b/src/network/uploadthread.py
@@ -6,10 +6,10 @@ import time
import random
import protocol
import state
-import connectionpool
+from network import connectionpool
from randomtrackingdict import RandomTrackingDict
from network import dandelion_ins
-from threads import StoppableThread
+from .threads import StoppableThread
class UploadThread(StoppableThread):
@@ -50,7 +50,7 @@ class UploadThread(StoppableThread):
break
try:
payload.extend(protocol.CreatePacket(
- 'object', state.Inventory[chunk].payload))
+ b'object', state.Inventory[chunk].payload))
chunk_count += 1
except KeyError:
i.antiIntersectionDelay()
diff --git a/src/plugins/indicator_libmessaging.py b/src/plugins/indicator_libmessaging.py
index b471d2ef..4cd583e3 100644
--- a/src/plugins/indicator_libmessaging.py
+++ b/src/plugins/indicator_libmessaging.py
@@ -23,9 +23,9 @@ class IndicatorLibmessaging(object):
return
self._menu = {
- 'send': unicode(_translate('MainWindow', 'Send')),
- 'messages': unicode(_translate('MainWindow', 'Messages')),
- 'subscriptions': unicode(_translate('MainWindow', 'Subscriptions'))
+ 'send': _translate('MainWindow', 'Send'),
+ 'messages': _translate('MainWindow', 'Messages'),
+ 'subscriptions': _translate('MainWindow', 'Subscriptions')
}
self.new_message_item = self.new_broadcast_item = None
@@ -45,12 +45,11 @@ class IndicatorLibmessaging(object):
def show_unread(self, draw_attention=False):
"""
- show the number of unread messages and subscriptions
+ Show the number of unread messages and subscriptions
on the messaging menu
"""
for source, count in zip(
- ('messages', 'subscriptions'),
- self.form.getUnread()
+ ('messages', 'subscriptions'), self.form.getUnread()
):
if count > 0:
if self.app.has_source(source):
diff --git a/src/plugins/menu_qrcode.py b/src/plugins/menu_qrcode.py
index ea322a49..16bbef45 100644
--- a/src/plugins/menu_qrcode.py
+++ b/src/plugins/menu_qrcode.py
@@ -3,10 +3,10 @@
A menu plugin showing QR-Code for bitmessage address in modal dialog.
"""
-import urllib
+from six.moves.urllib.parse import urlencode
import qrcode
-from PyQt4 import QtCore, QtGui
+from qtpy import QtGui, QtCore, QtWidgets
from pybitmessage.tr import _translate
@@ -39,23 +39,23 @@ class Image(qrcode.image.base.BaseImage): # pylint: disable=abstract-method
QtCore.Qt.black)
-class QRCodeDialog(QtGui.QDialog):
+class QRCodeDialog(QtWidgets.QDialog):
"""The dialog"""
def __init__(self, parent):
super(QRCodeDialog, self).__init__(parent)
- self.image = QtGui.QLabel(self)
- self.label = QtGui.QLabel(self)
+ self.image = QtWidgets.QLabel(self)
+ self.label = QtWidgets.QLabel(self)
font = QtGui.QFont()
font.setBold(True)
font.setWeight(75)
self.label.setFont(font)
self.label.setAlignment(
QtCore.Qt.AlignLeft | QtCore.Qt.AlignVCenter)
- buttonBox = QtGui.QDialogButtonBox(self)
+ buttonBox = QtWidgets.QDialogButtonBox(self)
buttonBox.setOrientation(QtCore.Qt.Horizontal)
- buttonBox.setStandardButtons(QtGui.QDialogButtonBox.Ok)
+ buttonBox.setStandardButtons(QtWidgets.QDialogButtonBox.Ok)
buttonBox.accepted.connect(self.accept)
- layout = QtGui.QVBoxLayout(self)
+ layout = QtWidgets.QVBoxLayout(self)
layout.addWidget(self.image)
layout.addWidget(self.label)
layout.addWidget(buttonBox)
@@ -72,7 +72,7 @@ class QRCodeDialog(QtGui.QDialog):
self.label.setText(text)
self.label.setToolTip(text)
self.label.setFixedWidth(pixmap.width())
- self.setFixedSize(QtGui.QWidget.sizeHint(self))
+ self.setFixedSize(QtWidgets.QWidget.sizeHint(self))
def connect_plugin(form):
@@ -93,7 +93,7 @@ def connect_plugin(form):
return
dialog.render(
'bitmessage:%s' % account.address + (
- '?' + urllib.urlencode({'label': label.encode('utf-8')})
+ '?' + urlencode({'label': label.encode('utf-8')})
if label != account.address else '')
)
dialog.exec_()
diff --git a/src/plugins/plugin.py b/src/plugins/plugin.py
index 629de0a6..9f3b76c0 100644
--- a/src/plugins/plugin.py
+++ b/src/plugins/plugin.py
@@ -32,6 +32,7 @@ def get_plugins(group, point='', name=None, fallback=None):
except (AttributeError,
ImportError,
ValueError,
+ RuntimeError, # PyQt for example
pkg_resources.DistributionNotFound,
pkg_resources.UnknownExtra):
logger.debug(
diff --git a/src/protocol.py b/src/protocol.py
index 96c980bb..cf656bc8 100644
--- a/src/protocol.py
+++ b/src/protocol.py
@@ -12,6 +12,8 @@ import sys
import time
from binascii import hexlify
from struct import Struct, pack, unpack
+import six
+import sqlite3
import defaults
import highlevelcrypto
@@ -23,6 +25,7 @@ from debug import logger
from helper_sql import sqlExecute
from network.node import Peer
from version import softwareVersion
+from dbcompat import dbstr
# Network constants
magic = 0xE9BEB4D9
@@ -168,11 +171,11 @@ def checkIPAddress(host, private=False):
otherwise returns False
"""
if host[0:12] == b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xFF\xFF':
- hostStandardFormat = socket.inet_ntop(socket.AF_INET, host[12:])
+ hostStandardFormat = socket.inet_ntop(socket.AF_INET, bytes(host[12:]))
return checkIPv4Address(host[12:], hostStandardFormat, private)
elif host[0:6] == b'\xfd\x87\xd8\x7e\xeb\x43':
# Onion, based on BMD/bitcoind
- hostStandardFormat = base64.b32encode(host[6:]).lower() + ".onion"
+ hostStandardFormat = base64.b32encode(host[6:]).lower() + b".onion"
if private:
return False
return hostStandardFormat
@@ -227,7 +230,7 @@ def checkIPv6Address(host, hostStandardFormat, private=False):
logger.debug('Ignoring loopback address: %s', hostStandardFormat)
return False
try:
- host = [ord(c) for c in host[:2]]
+ host = [six.byte2int(c) for c in host[:2]]
except TypeError: # python3 has ints already
pass
if host[0] == 0xfe and host[1] & 0xc0 == 0x80:
@@ -293,7 +296,7 @@ def isProofOfWorkSufficient(
if TTL < 300:
TTL = 300
POW, = unpack('>Q', highlevelcrypto.double_sha512(
- data[:8] + hashlib.sha512(data[8:]).digest())[0:8])
+ bytes(data[:8]) + hashlib.sha512(data[8:]).digest())[0:8])
return POW <= 2 ** 64 / (
nonceTrialsPerByte * (
len(data) + payloadLengthExtraBytes
@@ -417,7 +420,7 @@ def assembleVersionMessage( # pylint: disable=too-many-arguments
return CreatePacket(b'version', payload)
-def assembleErrorMessage(fatal=0, banTime=0, inventoryVector='', errorText=''):
+def assembleErrorMessage(fatal=0, banTime=0, inventoryVector=b'', errorText=''):
"""
Construct the payload of an error message,
return the resulting bytes of running `CreatePacket` on it
@@ -426,6 +429,8 @@ def assembleErrorMessage(fatal=0, banTime=0, inventoryVector='', errorText=''):
payload += encodeVarint(banTime)
payload += encodeVarint(len(inventoryVector))
payload += inventoryVector
+ if isinstance(errorText, str):
+ errorText = errorText.encode("utf-8", "replace")
payload += encodeVarint(len(errorText))
payload += errorText
return CreatePacket(b'error', payload)
@@ -465,7 +470,7 @@ def decryptAndCheckPubkeyPayload(data, address):
readPosition += varintLength
# We'll store the address version and stream number
# (and some more) in the pubkeys table.
- storedData = data[20:readPosition]
+ storedData = bytes(data[20:readPosition])
if addressVersion != embeddedAddressVersion:
logger.info(
@@ -482,11 +487,11 @@ def decryptAndCheckPubkeyPayload(data, address):
readPosition += 32
# the time through the tag. More data is appended onto
# signedData below after the decryption.
- signedData = data[8:readPosition]
+ signedData = bytes(data[8:readPosition])
encryptedData = data[readPosition:]
# Let us try to decrypt the pubkey
- toAddress, cryptorObject = state.neededPubkeys[tag]
+ toAddress, cryptorObject = state.neededPubkeys[bytes(tag)]
if toAddress != address:
logger.critical(
'decryptAndCheckPubkeyPayload failed due to toAddress'
@@ -511,9 +516,9 @@ def decryptAndCheckPubkeyPayload(data, address):
readPosition = 0
# bitfieldBehaviors = decryptedData[readPosition:readPosition + 4]
readPosition += 4
- pubSigningKey = '\x04' + decryptedData[readPosition:readPosition + 64]
+ pubSigningKey = b'\x04' + decryptedData[readPosition:readPosition + 64]
readPosition += 64
- pubEncryptionKey = '\x04' + decryptedData[readPosition:readPosition + 64]
+ pubEncryptionKey = b'\x04' + decryptedData[readPosition:readPosition + 64]
readPosition += 64
specifiedNonceTrialsPerByteLength = decodeVarint(
decryptedData[readPosition:readPosition + 10])[1]
@@ -558,7 +563,7 @@ def decryptAndCheckPubkeyPayload(data, address):
hexlify(pubSigningKey), hexlify(pubEncryptionKey)
)
- t = (address, addressVersion, storedData, int(time.time()), 'yes')
+ t = (dbstr(address), addressVersion, sqlite3.Binary(storedData), int(time.time()), dbstr('yes'))
sqlExecute('''INSERT INTO pubkeys VALUES (?,?,?,?,?)''', *t)
return 'successful'
except varintDecodeError:
diff --git a/src/py3bitmessage b/src/py3bitmessage
new file mode 100755
index 00000000..6277ea32
--- /dev/null
+++ b/src/py3bitmessage
@@ -0,0 +1,13 @@
+#!/usr/bin/python3
+
+import os
+import pkg_resources
+
+import pybitmessage
+
+dist = pkg_resources.get_distribution('pybitmessage')
+script_file = os.path.join(dist.location, dist.key, 'bitmessagemain.py')
+new_globals = globals()
+new_globals.update(__file__=script_file)
+
+execfile(script_file, new_globals)
diff --git a/src/pybitmessage b/src/pybitmessage
old mode 100644
new mode 100755
diff --git a/src/pyelliptic/arithmetic.py b/src/pyelliptic/arithmetic.py
index 23c24b5e..ce66db98 100644
--- a/src/pyelliptic/arithmetic.py
+++ b/src/pyelliptic/arithmetic.py
@@ -3,6 +3,7 @@ Arithmetic Expressions
"""
import hashlib
import re
+import six
P = 2**256 - 2**32 - 2**9 - 2**8 - 2**7 - 2**6 - 2**4 - 1
A = 0
@@ -34,7 +35,7 @@ def get_code_string(base):
return b'123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz'
if base == 256:
try:
- return b''.join([chr(x) for x in range(256)])
+ return b''.join([six.int2byte(x) for x in range(256)])
except TypeError:
return bytes([x for x in range(256)])
diff --git a/src/pyelliptic/cipher.py b/src/pyelliptic/cipher.py
index af6c08ca..2c2c54da 100644
--- a/src/pyelliptic/cipher.py
+++ b/src/pyelliptic/cipher.py
@@ -30,7 +30,7 @@ class Cipher(object):
self.ctx = OpenSSL.EVP_CIPHER_CTX_new()
if do == 1 or do == 0:
k = OpenSSL.malloc(key, len(key))
- IV = OpenSSL.malloc(iv, len(iv))
+ IV = OpenSSL.malloc(bytes(iv), len(iv))
OpenSSL.EVP_CipherInit_ex(
self.ctx, self.cipher.get_pointer(), 0, k, IV, do)
else:
@@ -59,7 +59,7 @@ class Cipher(object):
"""Update result with more data"""
i = OpenSSL.c_int(0)
buffer = OpenSSL.malloc(b"", len(input) + self.cipher.get_blocksize())
- inp = OpenSSL.malloc(input, len(input))
+ inp = OpenSSL.malloc(bytes(input), len(input))
if OpenSSL.EVP_CipherUpdate(self.ctx, OpenSSL.byref(buffer),
OpenSSL.byref(i), inp, len(input)) == 0:
raise Exception("[OpenSSL] EVP_CipherUpdate FAIL ...")
diff --git a/src/pyelliptic/ecc.py b/src/pyelliptic/ecc.py
index c670d023..8f254561 100644
--- a/src/pyelliptic/ecc.py
+++ b/src/pyelliptic/ecc.py
@@ -7,6 +7,7 @@ Asymmetric cryptography using elliptic curves
from hashlib import sha512
from struct import pack, unpack
+from ctypes import c_char_p
from .cipher import Cipher
from .hash import equals, hmac_sha256
@@ -218,8 +219,8 @@ class ECC(object):
if other_key == 0:
raise Exception("[OpenSSL] EC_KEY_new_by_curve_name FAIL ...")
- other_pub_key_x = OpenSSL.BN_bin2bn(pubkey_x, len(pubkey_x), None)
- other_pub_key_y = OpenSSL.BN_bin2bn(pubkey_y, len(pubkey_y), None)
+ other_pub_key_x = OpenSSL.BN_bin2bn(c_char_p(bytes(pubkey_x)), len(pubkey_x), None)
+ other_pub_key_y = OpenSSL.BN_bin2bn(c_char_p(bytes(pubkey_y)), len(pubkey_y), None)
other_group = OpenSSL.EC_KEY_get0_group(other_key)
other_pub_key = OpenSSL.EC_POINT_new(other_group)
diff --git a/src/pyelliptic/hash.py b/src/pyelliptic/hash.py
index 70c9a6ce..76d0fd56 100644
--- a/src/pyelliptic/hash.py
+++ b/src/pyelliptic/hash.py
@@ -4,6 +4,8 @@ Wrappers for hash functions from OpenSSL.
# Copyright (C) 2011 Yann GUIBET
# See LICENSE for details.
+import six
+
from .openssl import OpenSSL
@@ -22,7 +24,7 @@ def _equals_str(a, b):
return False
result = 0
for x, y in zip(a, b):
- result |= ord(x) ^ ord(y)
+ result |= six.byte2int(x) ^ six.byte2int(y)
return result == 0
@@ -38,7 +40,7 @@ def hmac_sha256(k, m):
Compute the key and the message with HMAC SHA5256
"""
key = OpenSSL.malloc(k, len(k))
- d = OpenSSL.malloc(m, len(m))
+ d = OpenSSL.malloc(bytes(m), len(m))
md = OpenSSL.malloc(0, 32)
i = OpenSSL.pointer(OpenSSL.c_int(0))
OpenSSL.HMAC(OpenSSL.EVP_sha256(), key, len(k), d, len(m), md, i)
diff --git a/src/pyelliptic/openssl.py b/src/pyelliptic/openssl.py
index 851dfa15..2fb133a7 100644
--- a/src/pyelliptic/openssl.py
+++ b/src/pyelliptic/openssl.py
@@ -8,6 +8,7 @@ needed openssl functionality in class _OpenSSL.
"""
import ctypes
import sys
+import six
# pylint: disable=protected-access
@@ -691,7 +692,7 @@ class _OpenSSL(object):
length = self.BN_num_bytes(x)
data = self.malloc(0, length)
OpenSSL.BN_bn2bin(x, data)
- return ord(data[length - 1]) & 1
+ return six.byte2int(data[length - 1]) & 1
def get_cipher(self, name):
"""
@@ -745,7 +746,7 @@ class _OpenSSL(object):
"""
buffer_ = None
if data != 0:
- if sys.version_info.major == 3 and isinstance(data, type('')):
+ if six.PY3 and isinstance(data, type('')):
data = data.encode()
buffer_ = self.create_string_buffer(data, size)
else:
diff --git a/src/qidenticon.py b/src/qidenticon.py
index 13be3578..722c47ca 100644
--- a/src/qidenticon.py
+++ b/src/qidenticon.py
@@ -42,10 +42,7 @@ Returns an instance of :class:`QPixmap` which have generated identicon image.
from six.moves import range
-try:
- from PyQt5 import QtCore, QtGui
-except (ImportError, RuntimeError):
- from PyQt4 import QtCore, QtGui
+from qtpy import QtCore, QtGui
class IdenticonRendererBase(object):
@@ -129,11 +126,13 @@ class IdenticonRendererBase(object):
QtCore.QPointF(size, size), QtCore.QPointF(0., size)]
rotation = [0, 90, 180, 270]
- nopen = QtGui.QPen(foreColor, QtCore.Qt.NoPen)
+ nopen = QtGui.QPen(foreColor)
+ nopen.setStyle(QtCore.Qt.NoPen)
foreBrush = QtGui.QBrush(foreColor, QtCore.Qt.SolidPattern)
if penwidth > 0:
pen_color = QtGui.QColor(255, 255, 255)
- pen = QtGui.QPen(pen_color, QtCore.Qt.SolidPattern)
+ pen = QtGui.QPen(pen_color)
+ pen.setBrush(QtCore.Qt.SolidPattern)
pen.setWidth(penwidth)
painter = QtGui.QPainter()
diff --git a/src/randomtrackingdict.py b/src/randomtrackingdict.py
index 5bf19181..0944da2a 100644
--- a/src/randomtrackingdict.py
+++ b/src/randomtrackingdict.py
@@ -38,10 +38,10 @@ class RandomTrackingDict(object):
return self.len
def __contains__(self, key):
- return key in self.dictionary
+ return bytes(key) in self.dictionary
def __getitem__(self, key):
- return self.dictionary[key][1]
+ return self.dictionary[bytes(key)][1]
def _swap(self, i1, i2):
with self.lock:
@@ -49,26 +49,28 @@ class RandomTrackingDict(object):
key2 = self.indexDict[i2]
self.indexDict[i1] = key2
self.indexDict[i2] = key1
- self.dictionary[key1][0] = i2
- self.dictionary[key2][0] = i1
+ self.dictionary[bytes(key1)][0] = i2
+ self.dictionary[bytes(key2)][0] = i1
# for quick reassignment
return i2
def __setitem__(self, key, value):
with self.lock:
- if key in self.dictionary:
- self.dictionary[key][1] = value
+ key_bytes = bytes(key)
+ if key_bytes in self.dictionary:
+ self.dictionary[key_bytes][1] = value
else:
self.indexDict.append(key)
- self.dictionary[key] = [self.len, value]
+ self.dictionary[key_bytes] = [self.len, value]
self._swap(self.len, self.len - self.pendingLen)
self.len += 1
def __delitem__(self, key):
- if key not in self.dictionary:
+ key_bytes = bytes(key)
+ if key_bytes not in self.dictionary:
raise KeyError
with self.lock:
- index = self.dictionary[key][0]
+ index = self.dictionary[key_bytes][0]
# not pending
if index < self.len - self.pendingLen:
# left of pending part
@@ -82,7 +84,7 @@ class RandomTrackingDict(object):
# operation can improve 4x, but it's already very fast so we'll
# ignore it for the time being
del self.indexDict[-1]
- del self.dictionary[key]
+ del self.dictionary[key_bytes]
self.len -= 1
def setMaxPending(self, maxPending):
diff --git a/src/revert_blob_to_text.py b/src/revert_blob_to_text.py
new file mode 100644
index 00000000..bd38066d
--- /dev/null
+++ b/src/revert_blob_to_text.py
@@ -0,0 +1,52 @@
+import helper_startup
+import state
+import shutil
+import sqlite3
+
+expected_ver = 11
+
+print("Looking up database file..")
+helper_startup.loadConfig()
+db_path = state.appdata + "messages.dat"
+print("Database path: {}".format(db_path))
+db_backup_path = db_path + ".blob-keys"
+print("Backup path: {}".format(db_backup_path))
+shutil.copyfile(db_path, db_backup_path)
+print("Copied to backup")
+
+print()
+
+print("Open the database")
+conn = sqlite3.connect(db_path)
+cur = conn.cursor()
+
+cur.execute("SELECT value FROM settings WHERE key='version';")
+ver = int(cur.fetchall()[0][0])
+print("PyBitmessage database version: {}".format(ver))
+if ver != expected_ver:
+ print("Error: version must be {}".format(expected_ver))
+ conn.close()
+ print("Quitting..")
+ quit()
+print("Version OK")
+
+print()
+
+print("Converting..")
+q = "UPDATE inbox SET msgid=CAST(msgid AS TEXT), sighash=CAST(sighash AS TEXT);"
+print(q)
+cur.execute(q)
+q = "UPDATE pubkeys SET transmitdata=CAST(transmitdata AS TEXT);"
+print(q)
+cur.execute(q)
+q = "UPDATE sent SET msgid=CAST(msgid AS TEXT), toripe=CAST(toripe AS TEXT), ackdata=CAST(ackdata AS TEXT);"
+print(q)
+cur.execute(q)
+
+print("Commiting..")
+conn.commit()
+print("Conversion done")
+
+print("Close the database")
+conn.close()
+print("Finished")
diff --git a/src/shared.py b/src/shared.py
index b85ddb20..80147801 100644
--- a/src/shared.py
+++ b/src/shared.py
@@ -14,6 +14,7 @@ import stat
import subprocess # nosec B404
import sys
from binascii import hexlify
+from six.moves.reprlib import repr
# Project imports.
import highlevelcrypto
@@ -22,6 +23,7 @@ from addresses import decodeAddress, encodeVarint
from bmconfigparser import config
from debug import logger
from helper_sql import sqlQuery
+from dbcompat import dbstr
myECCryptorObjects = {}
@@ -37,8 +39,8 @@ broadcastSendersForWhichImWatching = {}
def isAddressInMyAddressBook(address):
"""Is address in my addressbook?"""
queryreturn = sqlQuery(
- '''select address from addressbook where address=?''',
- address)
+ '''select TRUE from addressbook where address=?''',
+ dbstr(address))
return queryreturn != []
@@ -46,8 +48,8 @@ def isAddressInMyAddressBook(address):
def isAddressInMySubscriptionsList(address):
"""Am I subscribed to this address?"""
queryreturn = sqlQuery(
- '''select * from subscriptions where address=?''',
- str(address))
+ '''select TRUE from subscriptions where address=?''',
+ dbstr(address))
return queryreturn != []
@@ -61,14 +63,14 @@ def isAddressInMyAddressBookSubscriptionsListOrWhitelist(address):
queryreturn = sqlQuery(
'''SELECT address FROM whitelist where address=?'''
''' and enabled = '1' ''',
- address)
+ dbstr(address))
if queryreturn != []:
return True
queryreturn = sqlQuery(
'''select address from subscriptions where address=?'''
''' and enabled = '1' ''',
- address)
+ dbstr(address))
if queryreturn != []:
return True
return False
@@ -114,11 +116,11 @@ def reloadMyAddressHashes():
if len(privEncryptionKey) == 64:
myECCryptorObjects[hashobj] = \
highlevelcrypto.makeCryptor(privEncryptionKey)
- myAddressesByHash[hashobj] = addressInKeysFile
+ myAddressesByHash[bytes(hashobj)] = addressInKeysFile
tag = highlevelcrypto.double_sha512(
encodeVarint(addressVersionNumber)
+ encodeVarint(streamNumber) + hashobj)[32:]
- myAddressesByTag[tag] = addressInKeysFile
+ myAddressesByTag[bytes(tag)] = addressInKeysFile
if not keyfileSecure:
fixSensitiveFilePermissions(os.path.join(
@@ -136,6 +138,7 @@ def reloadBroadcastSendersForWhichImWatching():
logger.debug('reloading subscriptions...')
for row in queryreturn:
address, = row
+ address = address.decode("utf-8", "replace")
# status
addressVersionNumber, streamNumber, hashobj = decodeAddress(address)[1:]
if addressVersionNumber == 2:
@@ -149,7 +152,7 @@ def reloadBroadcastSendersForWhichImWatching():
encodeVarint(addressVersionNumber)
+ encodeVarint(streamNumber) + hashobj
).digest()[:32]
- MyECSubscriptionCryptorObjects[hashobj] = \
+ MyECSubscriptionCryptorObjects[bytes(hashobj)] = \
highlevelcrypto.makeCryptor(hexlify(privEncryptionKey))
else:
doubleHashOfAddressData = highlevelcrypto.double_sha512(
@@ -158,7 +161,7 @@ def reloadBroadcastSendersForWhichImWatching():
)
tag = doubleHashOfAddressData[32:]
privEncryptionKey = doubleHashOfAddressData[:32]
- MyECSubscriptionCryptorObjects[tag] = \
+ MyECSubscriptionCryptorObjects[bytes(tag)] = \
highlevelcrypto.makeCryptor(hexlify(privEncryptionKey))
@@ -169,7 +172,7 @@ def fixPotentiallyInvalidUTF8Data(text):
return text
except UnicodeDecodeError:
return 'Part of the message is corrupt. The message cannot be' \
- ' displayed the normal way.\n\n' + repr(text)
+ ' displayed the normal way.\n\n' + text.decode("utf-8", "replace")
def checkSensitiveFilePermissions(filename):
@@ -193,7 +196,7 @@ def checkSensitiveFilePermissions(filename):
['/usr/bin/stat', '-f', '-c', '%T', filename],
stderr=subprocess.STDOUT
) # nosec B603
- if 'fuseblk' in fstype:
+ if b'fuseblk' in fstype:
logger.info(
'Skipping file permissions check for %s.'
' Filesystem fuseblk detected.', filename)
diff --git a/src/shutdown.py b/src/shutdown.py
index 441d655e..7b875f64 100644
--- a/src/shutdown.py
+++ b/src/shutdown.py
@@ -23,7 +23,11 @@ def doCleanShutdown():
objectProcessorQueue.put(('checkShutdownVariable', 'no data'))
for thread in threading.enumerate():
- if thread.isAlive() and isinstance(thread, StoppableThread):
+ try:
+ alive = thread.isAlive()
+ except AttributeError:
+ alive = thread.is_alive()
+ if alive and isinstance(thread, StoppableThread):
thread.stopThread()
UISignalQueue.put((
diff --git a/src/storage/sqlite.py b/src/storage/sqlite.py
index eb5df098..0064262d 100644
--- a/src/storage/sqlite.py
+++ b/src/storage/sqlite.py
@@ -1,6 +1,7 @@
"""
Sqlite Inventory
"""
+import six
import sqlite3
import time
from threading import RLock
@@ -29,20 +30,22 @@ class SqliteInventory(InventoryStorage):
def __contains__(self, hash_):
with self.lock:
- if hash_ in self._objects:
+ hash_bytes = bytes(hash_)
+ if hash_bytes in self._objects:
return True
rows = sqlQuery(
'SELECT streamnumber FROM inventory WHERE hash=?',
sqlite3.Binary(hash_))
if not rows:
return False
- self._objects[hash_] = rows[0][0]
+ self._objects[hash_bytes] = rows[0][0]
return True
def __getitem__(self, hash_):
with self.lock:
- if hash_ in self._inventory:
- return self._inventory[hash_]
+ hash_bytes = bytes(hash_)
+ if hash_bytes in self._inventory:
+ return self._inventory[hash_bytes]
rows = sqlQuery(
'SELECT objecttype, streamnumber, payload, expirestime, tag'
' FROM inventory WHERE hash=?', sqlite3.Binary(hash_))
@@ -53,15 +56,16 @@ class SqliteInventory(InventoryStorage):
def __setitem__(self, hash_, value):
with self.lock:
value = InventoryItem(*value)
- self._inventory[hash_] = value
- self._objects[hash_] = value.stream
+ hash_bytes = bytes(hash_)
+ self._inventory[hash_bytes] = value
+ self._objects[hash_bytes] = value.stream
def __delitem__(self, hash_):
raise NotImplementedError
def __iter__(self):
with self.lock:
- hashes = self._inventory.keys()[:]
+ hashes = [] + self._inventory.keys()[:]
hashes += (x for x, in sqlQuery('SELECT hash FROM inventory'))
return hashes.__iter__()
@@ -95,7 +99,7 @@ class SqliteInventory(InventoryStorage):
t = int(time.time())
hashes = [x for x, value in self._inventory.items()
if value.stream == stream and value.expires > t]
- hashes += (str(payload) for payload, in sqlQuery(
+ hashes += (bytes(payload) for payload, in sqlQuery(
'SELECT hash FROM inventory WHERE streamnumber=?'
' AND expirestime>?', stream, t))
return hashes
@@ -107,6 +111,10 @@ class SqliteInventory(InventoryStorage):
# always use the inventoryLock OUTSIDE of the sqlLock.
with SqlBulkExecute() as sql:
for objectHash, value in self._inventory.items():
+ tag = value[4]
+ if six.PY3 and isinstance(tag, str):
+ tag = tag.encode("utf-8", "replace")
+ value = [value[0], value[1], sqlite3.Binary(value[2]), value[3], sqlite3.Binary(tag)]
sql.execute(
'INSERT INTO inventory VALUES (?, ?, ?, ?, ?, ?)',
sqlite3.Binary(objectHash), *value)
diff --git a/src/storage/storage.py b/src/storage/storage.py
index 9b33eef7..d89be837 100644
--- a/src/storage/storage.py
+++ b/src/storage/storage.py
@@ -4,10 +4,7 @@ Storing inventory items
from abc import abstractmethod
from collections import namedtuple
-try:
- from collections import MutableMapping # pylint: disable=deprecated-class
-except ImportError:
- from collections.abc import MutableMapping
+from six.moves.collections_abc import MutableMapping # pylint: disable=deprecated-class
InventoryItem = namedtuple('InventoryItem', 'type stream payload expires tag')
diff --git a/src/tests/common.py b/src/tests/common.py
index 2d60c716..0cd84d2f 100644
--- a/src/tests/common.py
+++ b/src/tests/common.py
@@ -1,7 +1,7 @@
import os
-import sys
import time
import unittest
+import six
_files = (
@@ -33,7 +33,7 @@ def checkup():
def skip_python3():
"""Raise unittest.SkipTest() if detected python3"""
- if sys.hexversion >= 0x3000000:
+ if six.PY3:
raise unittest.SkipTest('Module is not ported to python3')
diff --git a/src/tests/core.py b/src/tests/core.py
index fd9b0d08..836117aa 100644
--- a/src/tests/core.py
+++ b/src/tests/core.py
@@ -6,7 +6,7 @@ Tests for core and those that do not work outside
import atexit
import os
import pickle # nosec
-import Queue
+from six.moves import queue as Queue
import random # nosec
import shutil
import socket
@@ -15,6 +15,8 @@ import sys
import threading
import time
import unittest
+import six
+import sqlite3
import protocol
import state
@@ -31,6 +33,7 @@ from network.node import Node, Peer
from network.tcp import Socks4aBMConnection, Socks5BMConnection, TCPConnection
from queues import excQueue
from version import softwareVersion
+from dbcompat import dbstr
from common import cleanup
@@ -137,8 +140,8 @@ class TestCore(unittest.TestCase):
@staticmethod
def _outdate_knownnodes():
with knownnodes.knownNodesLock:
- for nodes in knownnodes.knownNodes.itervalues():
- for node in nodes.itervalues():
+ for nodes in six.itervalues(knownnodes.knownNodes):
+ for node in six.itervalues(nodes):
node['lastseen'] -= 2419205 # older than 28 days
def test_knownnodes_pickle(self):
@@ -146,9 +149,9 @@ class TestCore(unittest.TestCase):
pickle_knownnodes()
self._wipe_knownnodes()
knownnodes.readKnownNodes()
- for nodes in knownnodes.knownNodes.itervalues():
+ for nodes in six.itervalues(knownnodes.knownNodes):
self_count = n = 0
- for n, node in enumerate(nodes.itervalues()):
+ for n, node in enumerate(six.itervalues(nodes)):
if node.get('self'):
self_count += 1
self.assertEqual(n - self_count, 2)
@@ -202,7 +205,7 @@ class TestCore(unittest.TestCase):
while c > 0:
time.sleep(1)
c -= 2
- for peer, con in connectionpool.pool.outboundConnections.iteritems():
+ for peer, con in six.iteritems(connectionpool.pool.outboundConnections):
if (
peer.host.startswith('bootstrap')
or peer.host == 'quzwelsuziwqgpt2.onion'
@@ -223,7 +226,7 @@ class TestCore(unittest.TestCase):
'Failed to connect during %.2f sec' % (time.time() - _started))
def _check_knownnodes(self):
- for stream in knownnodes.knownNodes.itervalues():
+ for stream in six.itervalues(knownnodes.knownNodes):
for peer in stream:
if peer.host.startswith('bootstrap'):
self.fail(
@@ -346,12 +349,18 @@ class TestCore(unittest.TestCase):
subject=subject, message=message
)
queryreturn = sqlQuery(
- '''select msgid from sent where ackdata=?''', result)
- self.assertNotEqual(queryreturn[0][0] if queryreturn else '', '')
+ '''select msgid from sent where ackdata=?''', sqlite3.Binary(result))
+ if len(queryreturn) < 1:
+ queryreturn = sqlQuery(
+ '''select msgid from sent where ackdata=CAST(? AS TEXT)''', result)
+ self.assertNotEqual(queryreturn[0][0] if queryreturn else b'', b'')
column_type = sqlQuery(
- '''select typeof(msgid) from sent where ackdata=?''', result)
- self.assertEqual(column_type[0][0] if column_type else '', 'text')
+ '''select typeof(msgid) from sent where ackdata=?''', sqlite3.Binary(result))
+ if len(column_type) < 1:
+ column_type = sqlQuery(
+ '''select typeof(msgid) from sent where ackdata=CAST(? AS TEXT)''', result)
+ self.assertEqual(column_type[0][0] if column_type else '', 'blob')
@unittest.skipIf(frozen, 'not packed test_pattern into the bundle')
def test_old_knownnodes_pickle(self):
@@ -369,7 +378,7 @@ class TestCore(unittest.TestCase):
@staticmethod
def delete_address_from_addressbook(address):
"""Clean up addressbook"""
- sqlQuery('''delete from addressbook where address=?''', address)
+ sqlQuery('''delete from addressbook where address=?''', dbstr(address))
def test_add_same_address_twice_in_addressbook(self):
"""checking same address is added twice in addressbook"""
@@ -383,7 +392,7 @@ class TestCore(unittest.TestCase):
"""checking is address added in addressbook or not"""
helper_addressbook.insert(label='test1', address=self.addr)
queryreturn = sqlQuery(
- 'select count(*) from addressbook where address=?', self.addr)
+ 'select count(*) from addressbook where address=?', dbstr(self.addr))
self.assertEqual(queryreturn[0][0], 1)
self.delete_address_from_addressbook(self.addr)
diff --git a/src/tests/partial.py b/src/tests/partial.py
index 870f6626..b5543b71 100644
--- a/src/tests/partial.py
+++ b/src/tests/partial.py
@@ -3,6 +3,7 @@
import os
import sys
import unittest
+import six
from pybitmessage import pathmagic
@@ -22,7 +23,7 @@ class TestPartialRun(unittest.TestCase):
import state
from debug import logger # noqa:F401 pylint: disable=unused-variable
- if sys.hexversion >= 0x3000000:
+ if six.PY3:
# pylint: disable=no-name-in-module,relative-import
from mockbm import network as network_mock
import network
diff --git a/src/tests/test_api.py b/src/tests/test_api.py
index 0df145bc..94fc8e23 100644
--- a/src/tests/test_api.py
+++ b/src/tests/test_api.py
@@ -22,7 +22,10 @@ from .test_process import TestProcessProto
class TestAPIProto(TestProcessProto):
"""Test case logic for testing API"""
- _process_cmd = ['pybitmessage', '-t']
+ if six.PY3:
+ _process_cmd = ['./py3bitmessage', '-t']
+ else: # assume six.PY2
+ _process_cmd = ['./pybitmessage', '-t']
@classmethod
def setUpClass(cls):
@@ -57,7 +60,10 @@ class TestAPIShutdown(TestAPIProto):
class TestAPI(TestAPIProto):
"""Main API test case"""
- _seed = base64.encodestring(sample_seed)
+ if six.PY3:
+ _seed = base64.encodebytes(sample_seed)
+ else: # assume six.PY2
+ _seed = base64.encodestring(sample_seed)
def _add_random_address(self, label):
addr = self.api.createRandomAddress(base64.encodestring(label))
@@ -242,7 +248,7 @@ class TestAPI(TestAPIProto):
msg_subject = base64.encodestring('test_subject')
result = self.api.sendMessage(
sample_deterministic_addr4, addr, msg_subject, msg)
- self.assertNotRegexpMatches(result, r'^API Error')
+ six.assertNotRegex(self, result, r'^API Error')
self.api.deleteAddress(addr)
# Remove known address
self.api.deleteAddressBookEntry(sample_deterministic_addr4)
diff --git a/src/tests/test_api_thread.py b/src/tests/test_api_thread.py
index 6e453b19..6fc3b66f 100644
--- a/src/tests/test_api_thread.py
+++ b/src/tests/test_api_thread.py
@@ -1,9 +1,9 @@
"""TestAPIThread class definition"""
-import sys
import time
from binascii import hexlify, unhexlify
from struct import pack
+import six
from six.moves import queue, xmlrpc_client
@@ -68,7 +68,7 @@ class TestAPIThread(TestPartialRun):
def test_client_status(self):
"""Ensure the reply of clientStatus corresponds to mock"""
status = self.api.clientStatus()
- if sys.hexversion >= 0x3000000:
+ if six.PY3:
self.assertEqual(status["networkConnections"], 4)
self.assertEqual(status["pendingDownload"], 0)
diff --git a/src/tests/test_config_process.py b/src/tests/test_config_process.py
index 9322a2f0..b221aa6f 100644
--- a/src/tests/test_config_process.py
+++ b/src/tests/test_config_process.py
@@ -6,9 +6,6 @@ import os
import tempfile
from pybitmessage.bmconfigparser import config
from .test_process import TestProcessProto
-from .common import skip_python3
-
-skip_python3()
class TestProcessConfig(TestProcessProto):
diff --git a/src/tests/test_helper_inbox.py b/src/tests/test_helper_inbox.py
index a0b6de1b..31ccc208 100644
--- a/src/tests/test_helper_inbox.py
+++ b/src/tests/test_helper_inbox.py
@@ -26,7 +26,7 @@ class TestHelperInbox(unittest.TestCase):
def test_insert(self, mock_sql_execute): # pylint: disable=no-self-use
"""Test to perform an insert into the "inbox" table"""
mock_message_data = (
- "ruyv87bv",
+ b"ruyv87bv",
"BM-2cUGaEcGz9Zft1SPAo8FJtfzyADTpEgU9U",
"BM-2cUGaEcGz9Zft1SPAo8FJtfzyADTp5g99U",
"Test subject",
@@ -35,7 +35,7 @@ class TestHelperInbox(unittest.TestCase):
"inbox",
2,
0,
- "658gvjhtghv",
+ b"658gvjhtghv",
)
insert(t=mock_message_data)
mock_sql_execute.assert_called_once()
@@ -43,13 +43,15 @@ class TestHelperInbox(unittest.TestCase):
@patch("pybitmessage.helper_inbox.sqlExecute")
def test_trash(self, mock_sql_execute): # pylint: disable=no-self-use
"""Test marking a message in the `inbox` as `trash`"""
- mock_msg_id = "fefkosghsbse92"
+ mock_sql_execute.return_value = 1
+ mock_msg_id = b"fefkosghsbse92"
trash(msgid=mock_msg_id)
mock_sql_execute.assert_called_once()
@patch("pybitmessage.helper_inbox.sqlExecute")
def test_delete(self, mock_sql_execute): # pylint: disable=no-self-use
"""Test for permanent deletion of message from trash"""
+ mock_sql_execute.return_value = 1
mock_ack_data = genAckPayload()
delete(mock_ack_data)
mock_sql_execute.assert_called_once()
@@ -57,14 +59,15 @@ class TestHelperInbox(unittest.TestCase):
@patch("pybitmessage.helper_inbox.sqlExecute")
def test_undeleteMessage(self, mock_sql_execute): # pylint: disable=no-self-use
"""Test for Undelete the message"""
- mock_msg_id = "fefkosghsbse92"
+ mock_sql_execute.return_value = 1
+ mock_msg_id = b"fefkosghsbse92"
undeleteMessage(msgid=mock_msg_id)
mock_sql_execute.assert_called_once()
@patch("pybitmessage.helper_inbox.sqlQuery")
def test_isMessageAlreadyInInbox(self, mock_sql_query):
"""Test for check for previous instances of this message"""
- fake_sigHash = "h4dkn54546"
+ fake_sigHash = b"h4dkn54546"
# if Message is already in Inbox
mock_sql_query.return_value = [(1,)]
result = isMessageAlreadyInInbox(sigHash=fake_sigHash)
diff --git a/src/tests/test_helper_sent.py b/src/tests/test_helper_sent.py
index 9227e43a..27e5d970 100644
--- a/src/tests/test_helper_sent.py
+++ b/src/tests/test_helper_sent.py
@@ -19,7 +19,7 @@ class TestHelperSent(unittest.TestCase):
"""Test insert with valid address"""
VALID_ADDRESS = "BM-2cUGaEcGz9Zft1SPAo8FJtfzyADTpEgU9U"
ackdata = insert(
- msgid="123456",
+ msgid=b"123456",
toAddress="[Broadcast subscribers]",
fromAddress=VALID_ADDRESS,
subject="Test Subject",
@@ -45,10 +45,12 @@ class TestHelperSent(unittest.TestCase):
@patch("pybitmessage.helper_sent.sqlExecute")
def test_delete(self, mock_sql_execute):
"""Test delete function"""
- delete("ack_data")
+ mock_sql_execute.return_value = 1
+ delete(b"ack_data")
self.assertTrue(mock_sql_execute.called)
+ import sqlite3
mock_sql_execute.assert_called_once_with(
- "DELETE FROM sent WHERE ackdata = ?", "ack_data"
+ "DELETE FROM sent WHERE ackdata = ?", sqlite3.Binary(b"ack_data")
)
@patch("pybitmessage.helper_sent.sqlQuery")
@@ -56,21 +58,21 @@ class TestHelperSent(unittest.TestCase):
"""Test retrieving valid message details"""
return_data = [
(
- "to@example.com",
- "from@example.com",
- "Test Subject",
- "Test Message",
- "2022-01-01",
+ b"to@example.com",
+ b"from@example.com",
+ b"Test Subject",
+ b"Test Message",
+ b"2022-01-01",
)
]
mock_sql_query.return_value = return_data
- result = retrieve_message_details("12345")
+ result = retrieve_message_details(b"12345")
self.assertEqual(result, return_data)
@patch("pybitmessage.helper_sent.sqlExecute")
def test_trash(self, mock_sql_execute):
"""Test marking a message as 'trash'"""
- ackdata = "ack_data"
+ ackdata = b"ack_data"
mock_sql_execute.return_value = 1
rowcount = trash(ackdata)
self.assertEqual(rowcount, 1)
diff --git a/src/tests/test_helper_sql.py b/src/tests/test_helper_sql.py
index 036bd2c9..a32f92c9 100644
--- a/src/tests/test_helper_sql.py
+++ b/src/tests/test_helper_sql.py
@@ -1,6 +1,7 @@
"""Test cases for helper_sql"""
import unittest
+import sqlite3
try:
# Python 3
@@ -23,23 +24,23 @@ class TestHelperSql(unittest.TestCase):
@patch("pybitmessage.helper_sql.sqlReturnQueue.get")
def test_sqlquery_no_args(self, mock_sqlreturnqueue_get, mock_sqlsubmitqueue_put):
"""Test sqlQuery with no additional arguments"""
- mock_sqlreturnqueue_get.return_value = ("dummy_result", None)
+ mock_sqlreturnqueue_get.return_value = (b"dummy_result", None)
result = helper_sql.sqlQuery(
"SELECT msgid FROM inbox where folder='inbox' ORDER BY received"
)
self.assertEqual(mock_sqlsubmitqueue_put.call_count, 2)
- self.assertEqual(result, "dummy_result")
+ self.assertEqual(result.decode("utf-8", "replace"), "dummy_result")
@patch("pybitmessage.helper_sql.sqlSubmitQueue.put")
@patch("pybitmessage.helper_sql.sqlReturnQueue.get")
def test_sqlquery_with_args(self, mock_sqlreturnqueue_get, mock_sqlsubmitqueue_put):
"""Test sqlQuery with additional arguments"""
- mock_sqlreturnqueue_get.return_value = ("dummy_result", None)
+ mock_sqlreturnqueue_get.return_value = (b"dummy_result", None)
result = helper_sql.sqlQuery(
"SELECT address FROM addressbook WHERE address=?", "PB-5yfds868gbkj"
)
self.assertEqual(mock_sqlsubmitqueue_put.call_count, 2)
- self.assertEqual(result, "dummy_result")
+ self.assertEqual(result.decode("utf-8", "replace"), "dummy_result")
@patch("pybitmessage.helper_sql.sqlSubmitQueue.put")
@patch("pybitmessage.helper_sql.sqlReturnQueue.get")
@@ -49,8 +50,14 @@ class TestHelperSql(unittest.TestCase):
rowcount = helper_sql.sqlExecute(
"UPDATE sent SET status = 'msgqueued'"
"WHERE ackdata = ? AND folder = 'sent'",
- "1710652313",
+ sqlite3.Binary(b"1710652313"),
)
+ if rowcount < 0:
+ rowcount = helper_sql.sqlExecute(
+ "UPDATE sent SET status = 'msgqueued'"
+ "WHERE ackdata = CAST(? AS TEXT) AND folder = 'sent'",
+ b"1710652313",
+ )
self.assertEqual(mock_sqlsubmitqueue_put.call_count, 3)
self.assertEqual(rowcount, 1)
@@ -80,7 +87,7 @@ class TestHelperSql(unittest.TestCase):
for i in range(0, ID_COUNT):
args.append("arg{}".format(i))
total_row_count_return = helper_sql.sqlExecuteChunked(
- "INSERT INTO table VALUES {}", ID_COUNT, *args
+ "INSERT INTO table VALUES {}", False, ID_COUNT, *args
)
self.assertEqual(TOTAL_ROW_COUNT, total_row_count_return)
self.assertTrue(mock_sqlsubmitqueue_put.called)
@@ -97,7 +104,7 @@ class TestHelperSql(unittest.TestCase):
for i in range(0, ID_COUNT):
args.append("arg{}".format(i))
total_row_count = helper_sql.sqlExecuteChunked(
- "INSERT INTO table VALUES {}", ID_COUNT, *args
+ "INSERT INTO table VALUES {}", False, ID_COUNT, *args
)
self.assertEqual(total_row_count, 0)
self.assertFalse(mock_sqlsubmitqueue_put.called)
@@ -112,7 +119,7 @@ class TestHelperSql(unittest.TestCase):
ID_COUNT = 12
args = ["args0", "arg1"]
total_row_count = helper_sql.sqlExecuteChunked(
- "INSERT INTO table VALUES {}", ID_COUNT, *args
+ "INSERT INTO table VALUES {}", False, ID_COUNT, *args
)
self.assertEqual(total_row_count, 0)
self.assertFalse(mock_sqlsubmitqueue_put.called)
diff --git a/src/tests/test_identicon.py b/src/tests/test_identicon.py
index 4c6be32d..35503d1c 100644
--- a/src/tests/test_identicon.py
+++ b/src/tests/test_identicon.py
@@ -4,7 +4,7 @@ import atexit
import unittest
try:
- from PyQt5 import QtGui, QtWidgets
+ from qtpy import QtGui, QtWidgets
from xvfbwrapper import Xvfb
from pybitmessage import qidenticon
except ImportError:
diff --git a/src/tests/test_inventory.py b/src/tests/test_inventory.py
index d0b9ff6d..30b7cad5 100644
--- a/src/tests/test_inventory.py
+++ b/src/tests/test_inventory.py
@@ -38,7 +38,7 @@ class TestFilesystemInventory(TestPartialRun):
embedded_time = int(time.time() + TTL)
msg = struct.pack('>Q', embedded_time) + os.urandom(166)
invhash = highlevelcrypto.calculateInventoryHash(msg)
- self.inventory[invhash] = (2, 1, msg, embedded_time, b'')
+ self.inventory[bytes(invhash)] = (2, 1, msg, embedded_time, b'')
@classmethod
def tearDownClass(cls):
diff --git a/src/tests/test_l10n.py b/src/tests/test_l10n.py
index c6988827..a61f2a99 100644
--- a/src/tests/test_l10n.py
+++ b/src/tests/test_l10n.py
@@ -1,9 +1,9 @@
"""Tests for l10n module"""
import re
-import sys
import time
import unittest
+import six
from pybitmessage import l10n
@@ -16,7 +16,7 @@ class TestL10n(unittest.TestCase):
self.assertFalse(re.search(r'\d', time.strftime("wrong")))
timestring_type = type(time.strftime(l10n.DEFAULT_TIME_FORMAT))
self.assertEqual(timestring_type, str)
- if sys.version_info[0] == 2:
+ if six.PY2:
self.assertEqual(timestring_type, bytes)
def test_getWindowsLocale(self):
diff --git a/src/tests/test_log.py b/src/tests/test_log.py
index 4e74e50d..0d05fcb7 100644
--- a/src/tests/test_log.py
+++ b/src/tests/test_log.py
@@ -1,8 +1,8 @@
"""Tests for logging"""
import subprocess
-import sys
import unittest
+import six
from pybitmessage import proofofwork
@@ -11,7 +11,7 @@ class TestLog(unittest.TestCase):
"""A test case for logging"""
@unittest.skipIf(
- sys.hexversion < 0x3000000, 'assertLogs is new in version 3.4')
+ six.PY2, 'assertLogs is new in version 3.4')
def test_LogOutput(self):
"""Use proofofwork.LogOutput to log output of a shell command"""
with self.assertLogs('default') as cm: # pylint: disable=no-member
diff --git a/src/tests/test_logger.py b/src/tests/test_logger.py
index 7fbb91c8..bf63a014 100644
--- a/src/tests/test_logger.py
+++ b/src/tests/test_logger.py
@@ -44,7 +44,7 @@ handlers=default
cls._files = cls._files[2:] + ('logging.dat',)
cls.log_file = os.path.join(cls.home, 'debug.log')
- with open(os.path.join(cls.home, 'logging.dat'), 'wb') as dst:
+ with open(os.path.join(cls.home, 'logging.dat'), 'w') as dst:
dst.write(cls.conf_template.format(cls.log_file, cls.pattern))
super(TestLogger, cls).setUpClass()
diff --git a/src/tests/test_network.py b/src/tests/test_network.py
index 206117e0..e54f737f 100644
--- a/src/tests/test_network.py
+++ b/src/tests/test_network.py
@@ -3,11 +3,8 @@
import threading
import time
-from .common import skip_python3
from .partial import TestPartialRun
-skip_python3()
-
class TestNetwork(TestPartialRun):
"""A test case for running the network subsystem"""
@@ -24,11 +21,14 @@ class TestNetwork(TestPartialRun):
# config variable is still used inside of the network ):
import network
from network import connectionpool, stats
+ from network.stats import sentBytes, receivedBytes
# beware of singleton
connectionpool.config = cls.config
cls.pool = connectionpool.pool
cls.stats = stats
+ cls.stats.sentBytes = sentBytes
+ cls.stats.receivedBytes = receivedBytes
network.start(cls.config, cls.state)
diff --git a/src/tests/test_process.py b/src/tests/test_process.py
index 37b34541..9101fe7c 100644
--- a/src/tests/test_process.py
+++ b/src/tests/test_process.py
@@ -11,6 +11,7 @@ import time
import unittest
import psutil
+import six
from .common import cleanup, put_signal_file, skip_python3
@@ -22,7 +23,10 @@ class TestProcessProto(unittest.TestCase):
"""Test case implementing common logic for external testing:
it starts pybitmessage in setUpClass() and stops it in tearDownClass()
"""
- _process_cmd = ['pybitmessage', '-d']
+ if six.PY3:
+ _process_cmd = ['./py3bitmessage', '-d']
+ else: # assume six.PY2
+ _process_cmd = ['./pybitmessage', '-d']
_threads_count_min = 15
_threads_count_max = 16
_threads_names = [
diff --git a/src/tests/test_protocol.py b/src/tests/test_protocol.py
index 69e1e82f..9aeb8e09 100644
--- a/src/tests/test_protocol.py
+++ b/src/tests/test_protocol.py
@@ -2,8 +2,8 @@
Tests for common protocol functions
"""
-import sys
import unittest
+import six
from pybitmessage import protocol, state
from pybitmessage.helper_startup import fixSocket
@@ -81,7 +81,7 @@ class TestProtocol(TestSocketInet):
self.assertEqual(protocol.checkIPAddress(globalhost), '8.8.8.8')
@unittest.skipIf(
- sys.hexversion >= 0x3000000, 'this is still not working with python3')
+ six.PY3, 'this is still not working with python3')
def test_check_local_socks(self):
"""The SOCKS part of the local check"""
self.assertTrue(
diff --git a/src/tests/test_randomtrackingdict.py b/src/tests/test_randomtrackingdict.py
index 2db3c423..cbe0ee55 100644
--- a/src/tests/test_randomtrackingdict.py
+++ b/src/tests/test_randomtrackingdict.py
@@ -15,10 +15,10 @@ class TestRandomTrackingDict(unittest.TestCase):
@staticmethod
def randString():
"""helper function for tests, generates a random string"""
- retval = ''
- for _ in range(32):
- retval += chr(random.randint(0, 255))
- return retval
+ retval = bytearray(32)
+ for i in range(32):
+ retval[i] = random.randint(0, 255)
+ return bytes(retval)
def test_check_randomtrackingdict(self):
"""Check the logic of RandomTrackingDict class"""
diff --git a/src/tests/test_shared.py b/src/tests/test_shared.py
index 39bedf32..f53930a1 100644
--- a/src/tests/test_shared.py
+++ b/src/tests/test_shared.py
@@ -46,7 +46,8 @@ class TestShared(unittest.TestCase):
address = sample_address
# if address is in MyAddressbook
- mock_sql_query.return_value = [address]
+ TRUE = 1
+ mock_sql_query.return_value = [TRUE]
return_val = isAddressInMyAddressBook(address)
mock_sql_query.assert_called_once()
self.assertTrue(return_val)
@@ -64,7 +65,8 @@ class TestShared(unittest.TestCase):
address = sample_address
# if address is in MySubscriptionsList
- mock_sql_query.return_value = [address]
+ TRUE = 1
+ mock_sql_query.return_value = [TRUE]
return_val = isAddressInMySubscriptionsList(address)
self.assertTrue(return_val)
@@ -78,7 +80,7 @@ class TestShared(unittest.TestCase):
def test_reloadBroadcastSendersForWhichImWatching(self, mock_sql_query):
"""Test for reload Broadcast Senders For Which Im Watching"""
mock_sql_query.return_value = [
- (sample_address,),
+ (bytes(sample_address.encode("utf-8", "replace")),),
]
# before reload
self.assertEqual(len(MyECSubscriptionCryptorObjects), 0)
diff --git a/src/tests/test_sqlthread.py b/src/tests/test_sqlthread.py
index a612df3a..7c6318e6 100644
--- a/src/tests/test_sqlthread.py
+++ b/src/tests/test_sqlthread.py
@@ -41,4 +41,4 @@ class TestSqlThread(unittest.TestCase):
query = sqlQuery('SELECT enaddr(4, 1, "21122112211221122112")')
self.assertEqual(
- query[0][-1], encoded_str, "test case fail for create_function")
+ query[0][-1].decode("utf-8", "replace"), encoded_str, "test case fail for create_function")
diff --git a/src/threads.py b/src/threads.py
index ac8bf7a6..ce781a77 100644
--- a/src/threads.py
+++ b/src/threads.py
@@ -14,6 +14,7 @@ There are also other threads in the `.network` package.
"""
import threading
+import six
from class_addressGenerator import addressGenerator
from class_objectProcessor import objectProcessor
@@ -32,12 +33,13 @@ else:
"""Set the thread name for external use (visible from the OS)."""
prctl.set_name(name)
- def _thread_name_hack(self):
- set_thread_name(self.name)
- threading.Thread.__bootstrap_original__(self)
- # pylint: disable=protected-access
- threading.Thread.__bootstrap_original__ = threading.Thread._Thread__bootstrap
- threading.Thread._Thread__bootstrap = _thread_name_hack
+ if six.PY2:
+ def _thread_name_hack(self):
+ set_thread_name(self.name)
+ threading.Thread.__bootstrap_original__(self)
+ # pylint: disable=protected-access
+ threading.Thread.__bootstrap_original__ = threading.Thread._Thread__bootstrap
+ threading.Thread._Thread__bootstrap = _thread_name_hack
printLock = threading.Lock()
diff --git a/src/tr.py b/src/tr.py
index eec82c37..d5178ceb 100644
--- a/src/tr.py
+++ b/src/tr.py
@@ -1,7 +1,8 @@
"""
-Translating text
+Slim layer providing environment agnostic _translate()
"""
-import os
+
+from unqstr import ustr
try:
import state
@@ -9,51 +10,17 @@ except ImportError:
from . import state
-class translateClass:
- """
- This is used so that the translateText function can be used
- when we are in daemon mode and not using any QT functions.
- """
- # pylint: disable=old-style-class,too-few-public-methods
- def __init__(self, context, text):
- self.context = context
- self.text = text
-
- def arg(self, _):
- """Replace argument placeholders"""
- if '%' in self.text:
- # This doesn't actually do anything with the arguments
- # because we don't have a UI in which to display this information anyway.
- return translateClass(self.context, self.text.replace('%', '', 1))
- return self.text
-
-
-def _translate(context, text, disambiguation=None, encoding=None, n=None):
+def _tr_dummy(context, text, disambiguation=None, n=None):
# pylint: disable=unused-argument
- return translateText(context, text, n)
+ return text
-def translateText(context, text, n=None):
- """Translate text in context"""
+if state.enableGUI and not state.curses:
try:
- enableGUI = state.enableGUI
- except AttributeError: # inside the plugin
- enableGUI = True
- if enableGUI:
- try:
- from PyQt4 import QtCore, QtGui
- except Exception as err:
- print('PyBitmessage requires PyQt unless you want to run it as a daemon'
- ' and interact with it using the API.'
- ' You can download PyQt from http://www.riverbankcomputing.com/software/pyqt/download'
- ' or by searching Google for \'PyQt Download\'.'
- ' If you want to run in daemon mode, see https://bitmessage.org/wiki/Daemon')
- print('Error message:', err)
- os._exit(0) # pylint: disable=protected-access
- if n is None:
- return QtGui.QApplication.translate(context, text)
- return QtGui.QApplication.translate(context, text, None, QtCore.QCoreApplication.CodecForTr, n)
+ from qtpy import QtWidgets, QtCore
+ except ImportError:
+ _translate = _tr_dummy
else:
- if '%' in text:
- return translateClass(context, text.replace('%', '', 1))
- return text
+ _translate = QtWidgets.QApplication.translate
+else:
+ _translate = _tr_dummy
diff --git a/src/translations/bitmessage_ar.qm b/src/translations/bitmessage_ar.qm
index 892f6160..6f279545 100644
Binary files a/src/translations/bitmessage_ar.qm and b/src/translations/bitmessage_ar.qm differ
diff --git a/src/translations/bitmessage_ar.ts b/src/translations/bitmessage_ar.ts
index 6bf906d7..4b3ba9c1 100644
--- a/src/translations/bitmessage_ar.ts
+++ b/src/translations/bitmessage_ar.ts
@@ -241,8 +241,8 @@ Please type the desired email address (including @mailchuck.com) below:
-
- واحد من العناوين، %1، حاصل على رقم إصدار 1، العناوين ذات رقم الإصدار 1 غير مدعومه حالياً، هل باستطاعتنا حذفه الآن؟
+
+ واحد من العناوين، {0}، حاصل على رقم إصدار 1، العناوين ذات رقم الإصدار 1 غير مدعومه حالياً، هل باستطاعتنا حذفه الآن؟
@@ -261,13 +261,13 @@ Please type the desired email address (including @mailchuck.com) below:
-
+
-
- تم إرسال الرسالة في %1
+
+ تم إرسال الرسالة في {0}
@@ -276,8 +276,8 @@ Please type the desired email address (including @mailchuck.com) below:
-
- تم استلام إشعار الاستلام للرسالة %1
+
+ تم استلام إشعار الاستلام للرسالة {0}
@@ -286,18 +286,18 @@ Please type the desired email address (including @mailchuck.com) below:
-
- البث في %1
+
+ البث في {0}
-
- مشكلة: العمل المطلوب من قبل المستلم أصعب من ما كنت مستعد للقيام به %1
+
+ مشكلة: العمل المطلوب من قبل المستلم أصعب من ما كنت مستعد للقيام به {0}
-
- مشكلة: مفتاح تشفير المرسل إليه غير جيد، لا يمكن تشفير الرسالة. %1
+
+ مشكلة: مفتاح تشفير المرسل إليه غير جيد، لا يمكن تشفير الرسالة. {0}
@@ -306,8 +306,8 @@ Please type the desired email address (including @mailchuck.com) below:
-
- حالة غير معروفه: %1 %2
+
+ حالة غير معروفه: {0} {1}
@@ -347,10 +347,10 @@ Please type the desired email address (including @mailchuck.com) below:
يمكنك إدارة مفاتيحك بواسطة تعديل ملف keys.dat المحفوظ في
-%1
+{0}
مهم جداً أن تحتفظ بنسخة إضافية من هذا الملف.
@@ -366,10 +366,10 @@ It is important that you back up this file.
يمكنك إدارة مفاتيحك بواسطة تعديل ملف keys.dat المحفوظ في
-%1
+{0}
مهم جداً أن تحتفظ بنسخة إضافية من هذا الملف. هل ترغب بفتح الملف الآن؟ تأكد من إغلاق البرنامج Bitmessage قبل تعديل الملف.
@@ -434,8 +434,8 @@ It is important that you back up this file. Would you like to open the file now?
-
- تم تكوين زمرة بنجاح، لإتاحة الفرصة للأخرين بالإنضمام لمجموعتك أعطهم إسم الزمرة و هذا العنوان %1، هذا العنوان سيظهر ضمن هوياتك.
+
+ تم تكوين زمرة بنجاح، لإتاحة الفرصة للأخرين بالإنضمام لمجموعتك أعطهم إسم الزمرة و هذا العنوان {0}، هذا العنوان سيظهر ضمن هوياتك.
@@ -502,53 +502,53 @@ It is important that you back up this file. Would you like to open the file now?
-
+
-
+
-
- خطأ: عناوين ال Bitmessage تبدأ ب BM-، يرجى فحص %1
+
+ خطأ: عناوين ال Bitmessage تبدأ ب BM-، يرجى فحص {0}
-
- خطأ: لم يتم إدخال أو نسخ العنوان %1 بطريقة صحيحة، يرجى فحصه.
+
+ خطأ: لم يتم إدخال أو نسخ العنوان {0} بطريقة صحيحة، يرجى فحصه.
-
- خطأ: العنوان %1 يحتوي على حروف غير صالحة، يرجى فحصه.
+
+ خطأ: العنوان {0} يحتوي على حروف غير صالحة، يرجى فحصه.
-
- خطأ: رقم إصدار العنوان %1 عالي جداً، إما أن تقوم بتحديث برنامج Bitmessage أو أن شريكك ذكي جدأ.
+
+ خطأ: رقم إصدار العنوان {0} عالي جداً، إما أن تقوم بتحديث برنامج Bitmessage أو أن شريكك ذكي جدأ.
-
- بعض البيانات المشفرة ضمن العنوان %1 قصيرة جداً. يمكن أن يكون هناك خطأ في برنامج شريكك.
+
+ بعض البيانات المشفرة ضمن العنوان {0} قصيرة جداً. يمكن أن يكون هناك خطأ في برنامج شريكك.
-
- بعض البيانات المشفرة ضمن العنوان %1 طويلة جداً. يمكن أن يكون هناك خطأ في برنامج شريكك.
+
+ بعض البيانات المشفرة ضمن العنوان {0} طويلة جداً. يمكن أن يكون هناك خطأ في برنامج شريكك.
-
+
-
- خطأ: هناك خطأ في هذا العنوان %1.
+
+ خطأ: هناك خطأ في هذا العنوان {0}.
@@ -562,8 +562,8 @@ It is important that you back up this file. Would you like to open the file now?
-
- بالنظر إلى العنوان %1, Bitmessage لم يستطع فهم رقم إصدار العنوان %2، ربما يجب عليك تحديث برنامج Bitmessage لإصداره الأخير.
+
+ بالنظر إلى العنوان {0}, Bitmessage لم يستطع فهم رقم إصدار العنوان {1}، ربما يجب عليك تحديث برنامج Bitmessage لإصداره الأخير.
@@ -572,8 +572,8 @@ It is important that you back up this file. Would you like to open the file now?
-
- بالنظر إلى العنوان %1, Bitmessage لم يستطع فهم رقم إصدار العنوان %2، ربما يجب عليك تحديث برنامج Bitmessage لإصداره الأخير.
+
+ بالنظر إلى العنوان {0}, Bitmessage لم يستطع فهم رقم إصدار العنوان {1}، ربما يجب عليك تحديث برنامج Bitmessage لإصداره الأخير.
@@ -707,8 +707,8 @@ It is important that you back up this file. Would you like to open the file now?
-
- لم يستطع Bitmessage العثور على عنوانك %1, ربما قمت بحذف العنوان؟
+
+ لم يستطع Bitmessage العثور على عنوانك {0}, ربما قمت بحذف العنوان؟
@@ -861,8 +861,8 @@ Are you sure you want to delete the channel?
-
- أنت تستخدم نقطة عبور TCP %1 - يمكنك تغييره في قائمة الضبط.
+
+ أنت تستخدم نقطة عبور TCP {0} - يمكنك تغييره في قائمة الضبط.
@@ -1056,7 +1056,7 @@ Are you sure you want to delete the channel?
-
+
@@ -1071,47 +1071,47 @@ Are you sure you want to delete the channel?
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
@@ -1140,7 +1140,7 @@ Are you sure you want to delete the channel?
-
+
@@ -1160,12 +1160,12 @@ Are you sure you want to delete the channel?
-
+
-
+
@@ -1175,7 +1175,7 @@ Are you sure you want to delete the channel?
-
+
@@ -1200,7 +1200,7 @@ Are you sure you want to delete the channel?
-
+
@@ -1220,7 +1220,7 @@ Are you sure you want to delete the channel?
-
+
@@ -1232,17 +1232,17 @@ There is no required difficulty for version 2 addresses like this.
+Receiver's required difficulty: {0} and {1}
-
+
-
+
@@ -1252,7 +1252,7 @@ Receiver's required difficulty: %1 and %2
-
+
@@ -1267,12 +1267,12 @@ Receiver's required difficulty: %1 and %2
-
+
-
+
@@ -1635,27 +1635,27 @@ The 'Random Number' option is selected by default but deterministic ad
-
+
-
+
-
+
-
+
-
+
diff --git a/src/translations/bitmessage_cs.qm b/src/translations/bitmessage_cs.qm
index c25ccafa..8fde0714 100644
Binary files a/src/translations/bitmessage_cs.qm and b/src/translations/bitmessage_cs.qm differ
diff --git a/src/translations/bitmessage_cs.ts b/src/translations/bitmessage_cs.ts
index 11ab163a..adcfd3d4 100644
--- a/src/translations/bitmessage_cs.ts
+++ b/src/translations/bitmessage_cs.ts
@@ -241,8 +241,8 @@ Please type the desired email address (including @mailchuck.com) below:
-
- Jedna z Vašich adres, %1, je stará adresa verze 1. Adresy verze 1 již nejsou podporovány. Můžeme ji nyní smazat?
+
+ Jedna z Vašich adres, {0}, je stará adresa verze 1. Adresy verze 1 již nejsou podporovány. Můžeme ji nyní smazat?
@@ -261,13 +261,13 @@ Please type the desired email address (including @mailchuck.com) below:
-
- Zpráva odeslána. Čekám na potvrzení. Odesláno v %1
+
+ Zpráva odeslána. Čekám na potvrzení. Odesláno v {0}
-
- Zpráva odeslána. Odesláno v %1
+
+ Zpráva odeslána. Odesláno v {0}
@@ -276,8 +276,8 @@ Please type the desired email address (including @mailchuck.com) below:
-
- Potvrzení o přijetí zprávy %1
+
+ Potvrzení o přijetí zprávy {0}
@@ -286,18 +286,18 @@ Please type the desired email address (including @mailchuck.com) below:
-
- Rozesláno v %1
+
+ Rozesláno v {0}
-
- Problém: Obtížnost práce požadovaná adresátem je vyšší než Vámi povolené maximum. %1
+
+ Problém: Obtížnost práce požadovaná adresátem je vyšší než Vámi povolené maximum. {0}
-
- Problém: Šifrovací klíč adresáta je nepoužitelný. Zprávu nelze zašifrovat. %1
+
+ Problém: Šifrovací klíč adresáta je nepoužitelný. Zprávu nelze zašifrovat. {0}
@@ -306,8 +306,8 @@ Please type the desired email address (including @mailchuck.com) below:
-
- Neznámý stav: %1 %2
+
+ Neznámý stav: {0} {1}
@@ -347,10 +347,10 @@ Please type the desired email address (including @mailchuck.com) below:
Své klíče můžete spravovat editováním souboru keys.dat, který najdete zde:
- %1
+ {0}
Je důležité si tento soubor zazálohovat.
@@ -366,10 +366,10 @@ Je důležité si tento soubor zazálohovat.
Své klíče můžete spravovat editováním souboru keys.dat, který najdete zde:
- %1
+ {0}
Je důležité si tento soubor zazálohovat. Přejete si tento soubor nyní otevřít? (Nezapomeňte zavřít Bitmessage předtím, než provedete jakékoli změny.)
@@ -434,8 +434,8 @@ Je důležité si tento soubor zazálohovat. Přejete si tento soubor nyní otev
-
- Kanál byl úspěšně vytvořen. Když chcete jiným lidem povolit připojit se k Vašemu kanálu, řekněte jim jméno kanálu a tuto adresu Bitmessage: %1. Tuto adresu také najdete v sekci "Vaše identity".
+
+ Kanál byl úspěšně vytvořen. Když chcete jiným lidem povolit připojit se k Vašemu kanálu, řekněte jim jméno kanálu a tuto adresu Bitmessage: {0}. Tuto adresu také najdete v sekci "Vaše identity".
@@ -502,53 +502,53 @@ Je důležité si tento soubor zazálohovat. Přejete si tento soubor nyní otev
-
- Zpráva, kterou se snažíte poslat, je o %1 bajtů delší, než je dovoleno. (Maximum je 261644 bajtů). Zkuste ji prosím před odesláním zkrátit.
+
+ Zpráva, kterou se snažíte poslat, je o {0} bajtů delší, než je dovoleno. (Maximum je 261644 bajtů). Zkuste ji prosím před odesláním zkrátit.
-
+
-
- Chyba: Adresy Bitmessage začínají na BM- Zkontroluje prosím %1
+
+ Chyba: Adresy Bitmessage začínají na BM- Zkontroluje prosím {0}
-
- Chyba: Adresa %1 nebyla správně opsána nebo zkopírována. Zkontrolujte ji prosím.
+
+ Chyba: Adresa {0} nebyla správně opsána nebo zkopírována. Zkontrolujte ji prosím.
-
- Chyba: Adresa %1 obsahuje neplatné znaky. Zkontrolujte ji prosím.
+
+ Chyba: Adresa {0} obsahuje neplatné znaky. Zkontrolujte ji prosím.
-
- Chyba: Verze adresy %1 je příliš vysoká. Buď používáte starou verzi Bitmessage a je čas na aktualizaci, nebo si Váš známý dělá legraci.
+
+ Chyba: Verze adresy {0} je příliš vysoká. Buď používáte starou verzi Bitmessage a je čas na aktualizaci, nebo si Váš známý dělá legraci.
-
- Chyba: Některá data zakódovaná v adrese %1 jsou příliš krátká. Možná je to chyba softwaru, který Váš známý používá.
+
+ Chyba: Některá data zakódovaná v adrese {0} jsou příliš krátká. Možná je to chyba softwaru, který Váš známý používá.
-
- Chyba: Některá data zakódovaná v adrese %1 jsou příliš dlouhá. Možná je to chyba softwaru, který Váš známý používá.
+
+ Chyba: Některá data zakódovaná v adrese {0} jsou příliš dlouhá. Možná je to chyba softwaru, který Váš známý používá.
-
- Chyba: Některá data zakódovaná v adrese %1 mají neplatný formát. Možná je to chyba softwaru, který Váš známý používá.
+
+ Chyba: Některá data zakódovaná v adrese {0} mají neplatný formát. Možná je to chyba softwaru, který Váš známý používá.
-
- Chyba: Nastal problém s adresou %1.
+
+ Chyba: Nastal problém s adresou {0}.
@@ -562,8 +562,8 @@ Je důležité si tento soubor zazálohovat. Přejete si tento soubor nyní otev
-
- Co se týče adresy %1, Bitmessage nerozumí jejímu číslu verze "%2". Možná byste měl(a) aktualizovat Bitmessage na nejnovější verzi.
+
+ Co se týče adresy {0}, Bitmessage nerozumí jejímu číslu verze "{1}". Možná byste měl(a) aktualizovat Bitmessage na nejnovější verzi.
@@ -572,8 +572,8 @@ Je důležité si tento soubor zazálohovat. Přejete si tento soubor nyní otev
-
- Co se týče adresy %1, Bitmessage neumí zpracovat její číslo proudu "%2". Možná byste měl(a) aktualizovat Bitmessage na nejnovější verzi.
+
+ Co se týče adresy {0}, Bitmessage neumí zpracovat její číslo proudu "{1}". Možná byste měl(a) aktualizovat Bitmessage na nejnovější verzi.
@@ -707,8 +707,8 @@ Je důležité si tento soubor zazálohovat. Přejete si tento soubor nyní otev
-
- Bitmessage nemůže najít Vaši adresu %1. Možná jste ji odstranil(a)?
+
+ Bitmessage nemůže najít Vaši adresu {0}. Možná jste ji odstranil(a)?
@@ -861,8 +861,8 @@ Are you sure you want to delete the channel?
-
- Používáte TCP port %1. (To lze změnit v nastavení).
+
+ Používáte TCP port {0}. (To lze změnit v nastavení).
@@ -1056,7 +1056,7 @@ Are you sure you want to delete the channel?
-
+
@@ -1071,47 +1071,47 @@ Are you sure you want to delete the channel?
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
@@ -1134,7 +1134,7 @@ Are you sure you want to delete the channel?
-
+
@@ -1154,12 +1154,12 @@ Are you sure you want to delete the channel?
-
+
-
+
@@ -1169,7 +1169,7 @@ Are you sure you want to delete the channel?
-
+
@@ -1194,7 +1194,7 @@ Are you sure you want to delete the channel?
-
+
@@ -1214,7 +1214,7 @@ Are you sure you want to delete the channel?
-
+
@@ -1226,17 +1226,17 @@ There is no required difficulty for version 2 addresses like this.
+Receiver's required difficulty: {0} and {1}
-
+
-
+
@@ -1246,7 +1246,7 @@ Receiver's required difficulty: %1 and %2
-
+
@@ -1261,12 +1261,12 @@ Receiver's required difficulty: %1 and %2
-
+
-
+
@@ -1629,27 +1629,27 @@ Možnost "Náhodné číslo" je nastavena jako výchozí, deterministi
-
+
-
+
-
+
-
+
-
+
diff --git a/src/translations/bitmessage_da.qm b/src/translations/bitmessage_da.qm
index e5588987..1b436601 100644
Binary files a/src/translations/bitmessage_da.qm and b/src/translations/bitmessage_da.qm differ
diff --git a/src/translations/bitmessage_da.ts b/src/translations/bitmessage_da.ts
index fcf80470..707967e7 100644
--- a/src/translations/bitmessage_da.ts
+++ b/src/translations/bitmessage_da.ts
@@ -241,8 +241,8 @@ Please type the desired email address (including @mailchuck.com) below:
-
- En af dine adresser, %1 er en gammel version 1-addresse. Version 1-addresser understøttes ikke længere. Må vi slette den?
+
+ En af dine adresser, {0} er en gammel version 1-addresse. Version 1-addresser understøttes ikke længere. Må vi slette den?
@@ -261,13 +261,13 @@ Please type the desired email address (including @mailchuck.com) below:
-
- Besked afsendt. Afventer bekræftelse på modtagelse. Sendt %1
+
+ Besked afsendt. Afventer bekræftelse på modtagelse. Sendt {0}
-
- Besked sendt. Sendt %1
+
+ Besked sendt. Sendt {0}
@@ -276,8 +276,8 @@ Please type the desired email address (including @mailchuck.com) below:
-
- Bekræftelse på modtagelse er modtaget %1
+
+ Bekræftelse på modtagelse er modtaget {0}
@@ -286,18 +286,18 @@ Please type the desired email address (including @mailchuck.com) below:
-
- Afsendt %1
+
+ Afsendt {0}
-
- Problem: Beregningen som kræves af modtageren er mere besværlig end du accepterer. %1
+
+ Problem: Beregningen som kræves af modtageren er mere besværlig end du accepterer. {0}
-
- Problem: Modtagerens krypteringsnøgle virker ikke. Beskeden kunne ikke krypteres. %1
+
+ Problem: Modtagerens krypteringsnøgle virker ikke. Beskeden kunne ikke krypteres. {0}
@@ -306,8 +306,8 @@ Please type the desired email address (including @mailchuck.com) below:
-
- Ukendt status: %1 %2
+
+ Ukendt status: {0} {1}
@@ -347,10 +347,10 @@ Please type the desired email address (including @mailchuck.com) below:
Du kan administrere dine nøgler ved at redigere keys.dat-filen i
-%1
+{0}
Det er vigtigt at tage backup af denne fil.
@@ -366,10 +366,10 @@ Det er vigtigt at tage backup af denne fil.
Du kan administrere dine nøgler ved at redigere keys.dat-filen i
-%1
+{0}
Det er vigtigt at tage backup af denne fil. (Sørg for at lukke Bitmessage før du foretager ændringer.)
@@ -434,8 +434,8 @@ Det er vigtigt at tage backup af denne fil. (Sørg for at lukke Bitmessage før
-
- Ny kanal oprettet. For at andre kan blive medlem skal du oplyse dem kanalnavnet og denne Bitmessage-adresse: %1. Denne adresse vises også i 'Dine identiteter'.
+
+ Ny kanal oprettet. For at andre kan blive medlem skal du oplyse dem kanalnavnet og denne Bitmessage-adresse: {0}. Denne adresse vises også i 'Dine identiteter'.
@@ -502,53 +502,53 @@ Det er vigtigt at tage backup af denne fil. (Sørg for at lukke Bitmessage før
-
- Beskeden som du prøver at sende er %1 byte for lang. (Den maksimale størrelse er 261644 byte). Prøv at gøre den kortere før afsendelsen.
+
+ Beskeden som du prøver at sende er {0} byte for lang. (Den maksimale størrelse er 261644 byte). Prøv at gøre den kortere før afsendelsen.
-
+
-
- Fejl: Bitmessage-adresser starter med BM- Check %1
+
+ Fejl: Bitmessage-adresser starter med BM- Check {0}
-
- Fejl: Adressen %1 er skrever eller kopieret forkert. Tjek den venligst.
+
+ Fejl: Adressen {0} er skrever eller kopieret forkert. Tjek den venligst.
-
- Fejl: Adressen %1 indeholder ugyldige tegn. Tjek den venligst.
+
+ Fejl: Adressen {0} indeholder ugyldige tegn. Tjek den venligst.
-
+
-
+
-
+
-
+
-
- Fejl: Der er noget galt med adressen %1.
+
+ Fejl: Der er noget galt med adressen {0}.
@@ -562,8 +562,8 @@ Det er vigtigt at tage backup af denne fil. (Sørg for at lukke Bitmessage før
-
- Vedrørende adressen %1, Bitmessage forstår ikke addreseversion %2. Måske bør du opgradere Bitmessage til den nyeste version.
+
+ Vedrørende adressen {0}, Bitmessage forstår ikke addreseversion {1}. Måske bør du opgradere Bitmessage til den nyeste version.
@@ -572,8 +572,8 @@ Det er vigtigt at tage backup af denne fil. (Sørg for at lukke Bitmessage før
-
- Vedrørende adressen %1, Bitmessage kan ikke håndtere flod nummer %2. Måske bør du opgradere Bitmessage til den nyeste version.
+
+ Vedrørende adressen {0}, Bitmessage kan ikke håndtere flod nummer {1}. Måske bør du opgradere Bitmessage til den nyeste version.
@@ -707,8 +707,8 @@ Det er vigtigt at tage backup af denne fil. (Sørg for at lukke Bitmessage før
-
- Bitmessage kan ikke finde din adresse %1. Måske har du fjernet den?
+
+ Bitmessage kan ikke finde din adresse {0}. Måske har du fjernet den?
@@ -865,8 +865,8 @@ Er du sikker på at du vil slette denne kanal?
-
- Du bruger TCP-port %1. (Dette kan ændres i indstillingerne).
+
+ Du bruger TCP-port {0}. (Dette kan ændres i indstillingerne).
@@ -1060,8 +1060,8 @@ Er du sikker på at du vil slette denne kanal?
-
- Zoom %1%
+
+ Zoom {0}%
@@ -1075,47 +1075,47 @@ Er du sikker på at du vil slette denne kanal?
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
@@ -1136,7 +1136,7 @@ Er du sikker på at du vil slette denne kanal?
-
+
@@ -1156,12 +1156,12 @@ Er du sikker på at du vil slette denne kanal?
-
+
-
+
@@ -1171,7 +1171,7 @@ Er du sikker på at du vil slette denne kanal?
-
+
@@ -1196,7 +1196,7 @@ Er du sikker på at du vil slette denne kanal?
-
+
@@ -1216,7 +1216,7 @@ Er du sikker på at du vil slette denne kanal?
-
+
@@ -1228,17 +1228,17 @@ There is no required difficulty for version 2 addresses like this.
+Receiver's required difficulty: {0} and {1}
-
+
-
+
@@ -1248,7 +1248,7 @@ Receiver's required difficulty: %1 and %2
-
+
@@ -1263,12 +1263,12 @@ Receiver's required difficulty: %1 and %2
-
+
-
+
@@ -1631,27 +1631,27 @@ Som standard er tilfældige tal valgt, men der er både fordele og ulemper ved a
-
+
-
+
-
+
-
+
-
+
diff --git a/src/translations/bitmessage_de.qm b/src/translations/bitmessage_de.qm
index ef443a61..e42c1760 100644
Binary files a/src/translations/bitmessage_de.qm and b/src/translations/bitmessage_de.qm differ
diff --git a/src/translations/bitmessage_de.ts b/src/translations/bitmessage_de.ts
index 69cdd2a8..ff23a30c 100644
--- a/src/translations/bitmessage_de.ts
+++ b/src/translations/bitmessage_de.ts
@@ -314,8 +314,8 @@ Please type the desired email address (including @mailchuck.com) below:
-
- Eine Ihrer Adressen, %1, ist eine alte Adresse der Version 1 und wird nicht mehr unterstützt. Soll sie jetzt gelöscht werden?
+
+ Eine Ihrer Adressen, {0}, ist eine alte Adresse der Version 1 und wird nicht mehr unterstützt. Soll sie jetzt gelöscht werden?
@@ -334,13 +334,13 @@ Please type the desired email address (including @mailchuck.com) below:
-
- Nachricht gesendet. Warte auf Bestätigung. Zeitpunkt der Sendung: %1
+
+ Nachricht gesendet. Warte auf Bestätigung. Zeitpunkt der Sendung: {0}
-
- Nachricht gesendet. Zeitpunkt der Sendung: %1
+
+ Nachricht gesendet. Zeitpunkt der Sendung: {0}
@@ -349,8 +349,8 @@ Please type the desired email address (including @mailchuck.com) below:
-
- Bestätigung der Nachricht erhalten %1
+
+ Bestätigung der Nachricht erhalten {0}
@@ -359,18 +359,18 @@ Please type the desired email address (including @mailchuck.com) below:
-
- Rundruf um %1
+
+ Rundruf um {0}
-
- Problem: Die vom Empfänger geforderte Arbeit ist schwerer als Sie bereit sind, zu berechnen. %1
+
+ Problem: Die vom Empfänger geforderte Arbeit ist schwerer als Sie bereit sind, zu berechnen. {0}
-
- Problem: Der Verschlüsselungscode des Empfängers ist nicht in Ordnung. Nachricht konnte nicht verschlüsselt werden. %1
+
+ Problem: Der Verschlüsselungscode des Empfängers ist nicht in Ordnung. Nachricht konnte nicht verschlüsselt werden. {0}
@@ -379,8 +379,8 @@ Please type the desired email address (including @mailchuck.com) below:
-
- Unbekannter Status: %1 %2
+
+ Unbekannter Status: {0} {1}
@@ -420,10 +420,10 @@ Please type the desired email address (including @mailchuck.com) below:
Sie können Ihre Schlüssel verwalten, indem Sie die keys.dat bearbeiten, die im Ordner
-%1 liegt.
+{0} liegt.
Es ist empfehlenswert, vorher ein Backup dieser Datei anzulegen.
@@ -439,10 +439,10 @@ Es ist empfehlenswert, vorher ein Backup dieser Datei anzulegen.
Sie können Ihre Schlüssel verwalten, indem Sie die keys.dat bearbeiten,
-die im Ordner %1 liegt.
+die im Ordner {0} liegt.
Es ist empfehlenswert, vorher ein Backup dieser Datei anzulegen. Möchten Sie die Datei jetzt öffnen?
(Stellen Sie sicher, dass Sie Bitmessage beendet haben, bevor Sie etwas ändern.)
@@ -508,7 +508,7 @@ Es ist empfehlenswert, vorher ein Backup dieser Datei anzulegen. Möchten Sie di
-
+
@@ -576,52 +576,52 @@ Es ist empfehlenswert, vorher ein Backup dieser Datei anzulegen. Möchten Sie di
-
- Die Nachricht, die Sie zu senden versuchen, ist %1 Byte zu lang. (Maximum 261.644 Bytes). Bitte verringern Sie ihre Größe vor dem Senden.
+
+ Die Nachricht, die Sie zu senden versuchen, ist {0} Byte zu lang. (Maximum 261.644 Bytes). Bitte verringern Sie ihre Größe vor dem Senden.
-
- Fehler: Ihr Konto war an keiner E-Mail Schnittstelle registriert. Registrierung als %1 wird versandt, bitte vor einem erneutem Sendeversuch auf die Registrierungsverarbeitung warten.
+
+ Fehler: Ihr Konto war an keiner E-Mail Schnittstelle registriert. Registrierung als {0} wird versandt, bitte vor einem erneutem Sendeversuch auf die Registrierungsverarbeitung warten.
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
@@ -636,8 +636,8 @@ Es ist empfehlenswert, vorher ein Backup dieser Datei anzulegen. Möchten Sie di
-
- Aufgrund der Adresse %1 kann Bitmessage Adressen mit der Version %2 nicht verarbeiten. Möglicherweise müssen Sie Bitmessage auf die aktuelle Version aktualisieren.
+
+ Aufgrund der Adresse {0} kann Bitmessage Adressen mit der Version {1} nicht verarbeiten. Möglicherweise müssen Sie Bitmessage auf die aktuelle Version aktualisieren.
@@ -646,8 +646,8 @@ Es ist empfehlenswert, vorher ein Backup dieser Datei anzulegen. Möchten Sie di
-
- Aufgrund der Adresse %1 kann Bitmessage den Datenstrom mit der Version %2 nicht verarbeiten. Möglicherweise müssen Sie Bitmessage auf die aktuelle Version aktualisieren.
+
+ Aufgrund der Adresse {0} kann Bitmessage den Datenstrom mit der Version {1} nicht verarbeiten. Möglicherweise müssen Sie Bitmessage auf die aktuelle Version aktualisieren.
@@ -781,8 +781,8 @@ Es ist empfehlenswert, vorher ein Backup dieser Datei anzulegen. Möchten Sie di
-
- Bitmessage kann Ihre Adresse %1 nicht finden. Haben Sie sie gelöscht?
+
+ Bitmessage kann Ihre Adresse {0} nicht finden. Haben Sie sie gelöscht?
@@ -939,7 +939,7 @@ Sind Sie sicher, dass Sie das Chan löschen möchten?
-
+
@@ -1134,8 +1134,8 @@ Sind Sie sicher, dass Sie das Chan löschen möchten?
-
- Zoom-Stufe %1%
+
+ Zoom-Stufe {0}%
@@ -1149,48 +1149,48 @@ Sind Sie sicher, dass Sie das Chan löschen möchten?
-
+
-
- Neue Version von PyBitmessage steht zur Verfügung: %1. Sie können sie von https://github.com/Bitmessage/PyBitmessage/releases/latest herunterladen.
+
+ Neue Version von PyBitmessage steht zur Verfügung: {0}. Sie können sie von https://github.com/Bitmessage/PyBitmessage/releases/latest herunterladen.
-
- Warte auf Abschluss von Berechnungen (PoW)... %1%
+
+ Warte auf Abschluss von Berechnungen (PoW)... {0}%
-
- PyBitmessage wird beendet... %1%
+
+ PyBitmessage wird beendet... {0}%
-
- Warte auf Versand von Objekten... %1%
+
+ Warte auf Versand von Objekten... {0}%
-
- Einstellungen werden gespeichert... %1%
+
+ Einstellungen werden gespeichert... {0}%
-
- Kern wird beendet... %1%
+
+ Kern wird beendet... {0}%
-
- Beende Benachrichtigungen... %1%
+
+ Beende Benachrichtigungen... {0}%
-
- Unmittelbar vor Beendung... %1%
+
+ Unmittelbar vor Beendung... {0}%
@@ -1204,8 +1204,8 @@ Sind Sie sicher, dass Sie das Chan löschen möchten?
-
- PyBitmessage wird beendet... %1%
+
+ PyBitmessage wird beendet... {0}%
@@ -1224,13 +1224,13 @@ Sind Sie sicher, dass Sie das Chan löschen möchten?
-
- Erzeuge %1 neue Addressen.
+
+ Erzeuge {0} neue Addressen.
-
- %1 befindet sich bereits unter Ihren Identitäten, wird nicht doppelt hinzugefügt.
+
+ {0} befindet sich bereits unter Ihren Identitäten, wird nicht doppelt hinzugefügt.
@@ -1239,7 +1239,7 @@ Sind Sie sicher, dass Sie das Chan löschen möchten?
-
+
@@ -1264,8 +1264,8 @@ Sind Sie sicher, dass Sie das Chan löschen möchten?
-
- Rundruf verschickt um %1
+
+ Rundruf verschickt um {0}
@@ -1284,7 +1284,7 @@ Sind Sie sicher, dass Sie das Chan löschen möchten?
-
+
Problem: Der Empfänger benutzt ein mobiles Gerät und erfordert eine unverschlüsselte Empfängeraddresse. Dies ist in Ihren Einstellungen jedoch nicht zulässig. 1%
@@ -1297,17 +1297,17 @@ Version-2-Addressen wie die des Empfängers haben keine Schweirigkeitserforderun
- Arbeit für Nachrichtenversand wird errichtet. Vom Empfänger geforderte Schwierigkeit: %1 und %2
+Receiver's required difficulty: {0} and {1}
+ Arbeit für Nachrichtenversand wird errichtet. Vom Empfänger geforderte Schwierigkeit: {0} und {1}
-
- Problem: Die vom Empfänger verlangte Arbeit (%1 und %2) ist schwieriger, als Sie in den Einstellungen erlaubt haben. %3
+
+ Problem: Die vom Empfänger verlangte Arbeit ({0} und {1}) ist schwieriger, als Sie in den Einstellungen erlaubt haben. {2}
-
+
Problem: Sie versuchen, eine Nachricht an sich zu versenden, aber Ihr Schlüssel befindet sich nicht in der keys.dat-Datei. Die Nachricht kann nicht verschlüsselt werden. 1%
@@ -1317,8 +1317,8 @@ Receiver's required difficulty: %1 and %2
-
- Nachricht gesendet. Auf Bestätigung wird gewartet. Zeitpunkt der Sendung: %1
+
+ Nachricht gesendet. Auf Bestätigung wird gewartet. Zeitpunkt der Sendung: {0}
@@ -1332,13 +1332,13 @@ Receiver's required difficulty: %1 and %2
-
- Nachfrage nach dem öffentlichen Schlüssel läuft, auf Antwort wird gewartet. Nachgefragt am %1
+
+ Nachfrage nach dem öffentlichen Schlüssel läuft, auf Antwort wird gewartet. Nachgefragt am {0}
-
- UPnP Port-Mapping eingerichtet auf Port %1
+
+ UPnP Port-Mapping eingerichtet auf Port {0}
@@ -1382,28 +1382,28 @@ Receiver's required difficulty: %1 and %2
-
- Kommunikationsfehler mit dem Proxy: %1. Bitte überprüfen Sie Ihre Netzwerkeinstellungen.
+
+ Kommunikationsfehler mit dem Proxy: {0}. Bitte überprüfen Sie Ihre Netzwerkeinstellungen.
-
- SOCKS5-Authentizierung fehlgeschlagen: %1. Bitte überprüfen Sie Ihre SOCKS5-Einstellungen.
+
+ SOCKS5-Authentizierung fehlgeschlagen: {0}. Bitte überprüfen Sie Ihre SOCKS5-Einstellungen.
-
- Die Uhrzeit ihres Computers, %1, ist möglicherweise falsch. Bitte überprüfen Sie Ihre einstellungen.
+
+ Die Uhrzeit ihres Computers, {0}, ist möglicherweise falsch. Bitte überprüfen Sie Ihre einstellungen.
-
- Der Name %1 wurde nicht gefunden.
+
+ Der Name {0} wurde nicht gefunden.
-
- Namecoin-abfrage fehlgeschlagen (%1)
+
+ Namecoin-abfrage fehlgeschlagen ({0})
@@ -1412,18 +1412,18 @@ Receiver's required difficulty: %1 and %2
-
- Der Name %1 beinhaltet keine gültige JSON-Daten.
+
+ Der Name {0} beinhaltet keine gültige JSON-Daten.
-
- Der Name %1 hat keine zugewiesene Bitmessageaddresse.
+
+ Der Name {0} hat keine zugewiesene Bitmessageaddresse.
-
- Erfolg! Namecoind Version %1 läuft.
+
+ Erfolg! Namecoind Version {0} läuft.
@@ -1481,53 +1481,53 @@ Willkommen zu einfachem und sicherem Bitmessage
-
- Fehler: Bitmessage Adressen starten mit BM- Bitte überprüfen Sie die Empfängeradresse %1
+
+ Fehler: Bitmessage Adressen starten mit BM- Bitte überprüfen Sie die Empfängeradresse {0}
-
- Fehler: Die Empfängeradresse %1 wurde nicht korrekt getippt oder kopiert. Bitte überprüfen.
+
+ Fehler: Die Empfängeradresse {0} wurde nicht korrekt getippt oder kopiert. Bitte überprüfen.
-
- Fehler: Die Empfängeradresse %1 beinhaltet ungültig Zeichen. Bitte überprüfen.
+
+ Fehler: Die Empfängeradresse {0} beinhaltet ungültig Zeichen. Bitte überprüfen.
-
- Fehler: Die Empfängerdresseversion von %1 ist zu hoch. Entweder Sie müssen Ihre Bitmessage Software aktualisieren oder Ihr Bekannter ist sehr clever.
+
+ Fehler: Die Empfängerdresseversion von {0} ist zu hoch. Entweder Sie müssen Ihre Bitmessage Software aktualisieren oder Ihr Bekannter ist sehr clever.
-
- Fehler: Einige Daten die in der Empfängerdresse %1 codiert sind, sind zu kurz. Es könnte sein, dass etwas mit der Software Ihres Bekannten nicht stimmt.
+
+ Fehler: Einige Daten die in der Empfängerdresse {0} codiert sind, sind zu kurz. Es könnte sein, dass etwas mit der Software Ihres Bekannten nicht stimmt.
-
- Fehler: Einige Daten die in der Empfängeradresse %1 codiert sind, sind zu lang. Es könnte sein, dass etwas mit der Software Ihres Bekannten nicht stimmt.
+
+ Fehler: Einige Daten die in der Empfängeradresse {0} codiert sind, sind zu lang. Es könnte sein, dass etwas mit der Software Ihres Bekannten nicht stimmt.
-
- Fehler: Einige codierte Daten in der Empfängeradresse %1 sind ungültig. Es könnte etwas mit der Software Ihres Bekannten sein.
+
+ Fehler: Einige codierte Daten in der Empfängeradresse {0} sind ungültig. Es könnte etwas mit der Software Ihres Bekannten sein.
-
- Fehler: Mit der Empfängeradresse %1 stimmt etwas nicht.
+
+ Fehler: Mit der Empfängeradresse {0} stimmt etwas nicht.
-
- Fehler: %1
+
+ Fehler: {0}
-
- Von %1
+
+ Von {0}
@@ -1572,7 +1572,7 @@ Willkommen zu einfachem und sicherem Bitmessage
- Den letzten %1 Rundruf von dieser Addresse anzeigen.Die letzten %1 Rundrufe von dieser Addresse anzeigen.
+ Den letzten {0} Rundruf von dieser Addresse anzeigen.Die letzten {0} Rundrufe von dieser Addresse anzeigen.
@@ -1619,8 +1619,8 @@ Willkommen zu einfachem und sicherem Bitmessage
-
- Der Link "%1" wird in Browser geöffnet. Es kann ein Sicherheitsrisiko darstellen, es könnte Sie de-anonymisieren oder schädliche Aktivitäten durchführen. Sind Sie sicher?
+
+ Der Link "{0}" wird in Browser geöffnet. Es kann ein Sicherheitsrisiko darstellen, es könnte Sie de-anonymisieren oder schädliche Aktivitäten durchführen. Sind Sie sicher?
@@ -1955,8 +1955,8 @@ Die Zufallszahlen-Option ist standardmässig gewählt, jedoch haben deterministi
-
- Sie benutzen TCP-Port %1 (Dieser kann in den Einstellungen verändert werden).
+
+ Sie benutzen TCP-Port {0} (Dieser kann in den Einstellungen verändert werden).
@@ -2008,28 +2008,28 @@ Die Zufallszahlen-Option ist standardmässig gewählt, jedoch haben deterministi
-
- Seit Start der Anwendung am %1
+
+ Seit Start der Anwendung am {0}
-
- Herunter: %1/s Insg.: %2
+
+ Herunter: {0}/s Insg.: {1}
-
- Hoch: %1/s Insg.: %2
+
+ Hoch: {0}/s Insg.: {1}
-
- Verbindungen insgesamt: %1
+
+ Verbindungen insgesamt: {0}
-
- Inventory lookups pro Sekunde: %1
+
+ Inventory lookups pro Sekunde: {0}
@@ -2194,8 +2194,8 @@ Die Zufallszahlen-Option ist standardmässig gewählt, jedoch haben deterministi
newchandialog
-
- Chan %1 erfolgreich erstellt/beigetreten
+
+ Chan {0} erfolgreich erstellt/beigetreten
diff --git a/src/translations/bitmessage_en.qm b/src/translations/bitmessage_en.qm
index 4751f4ca..71a25005 100644
Binary files a/src/translations/bitmessage_en.qm and b/src/translations/bitmessage_en.qm differ
diff --git a/src/translations/bitmessage_en.ts b/src/translations/bitmessage_en.ts
index 05e9cc4b..8525b52c 100644
--- a/src/translations/bitmessage_en.ts
+++ b/src/translations/bitmessage_en.ts
@@ -281,8 +281,8 @@ Please type the desired email address (including @mailchuck.com) below:
-
- One of your addresses, %1, is an old version 1 address. Version 1 addresses are no longer supported. May we delete it now?
+
+ One of your addresses, {0}, is an old version 1 address. Version 1 addresses are no longer supported. May we delete it now?
@@ -301,13 +301,13 @@ Please type the desired email address (including @mailchuck.com) below:
-
- Message sent. Waiting for acknowledgement. Sent at %1
+
+ Message sent. Waiting for acknowledgement. Sent at {0}
-
- Message sent. Sent at %1
+
+ Message sent. Sent at {0}
@@ -316,8 +316,8 @@ Please type the desired email address (including @mailchuck.com) below:
-
- Acknowledgement of the message received %1
+
+ Acknowledgement of the message received {0}
@@ -326,18 +326,18 @@ Please type the desired email address (including @mailchuck.com) below:
-
- Broadcast on %1
+
+ Broadcast on {0}
-
- Problem: The work demanded by the recipient is more difficult than you are willing to do. %1
+
+ Problem: The work demanded by the recipient is more difficult than you are willing to do. {0}
-
- Problem: The recipient's encryption key is no good. Could not encrypt message. %1
+
+ Problem: The recipient's encryption key is no good. Could not encrypt message. {0}
@@ -346,8 +346,8 @@ Please type the desired email address (including @mailchuck.com) below:
-
- Unknown status: %1 %2
+
+ Unknown status: {0} {1}
@@ -387,10 +387,10 @@ Please type the desired email address (including @mailchuck.com) below:
You may manage your keys by editing the keys.dat file stored in
- %1
+ {0}
It is important that you back up this file.
@@ -406,10 +406,10 @@ It is important that you back up this file.
You may manage your keys by editing the keys.dat file stored in
- %1
+ {0}
It 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.)
@@ -474,8 +474,8 @@ It is important that you back up this file. Would you like to open the file now?
-
- Successfully created chan. To let others join your chan, give them the chan name and this Bitmessage address: %1. This address also appears in 'Your Identities'.
+
+ Successfully created chan. To let others join your chan, give them the chan name and this Bitmessage address: {0}. This address also appears in 'Your Identities'.
@@ -545,53 +545,53 @@ It is important that you back up this file. Would you like to open the file now?
-
- The message that you are trying to send is too long by %1 bytes. (The maximum is 261644 bytes). Please cut it down before sending.
+
+ The message that you are trying to send is too long by {0} bytes. (The maximum is 261644 bytes). Please cut it down before sending.
-
- Error: Your account wasn't registered at an email gateway. Sending registration now as %1, please wait for the registration to be processed before retrying sending.
+
+ Error: Your account wasn't registered at an email gateway. Sending registration now as {0}, please wait for the registration to be processed before retrying sending.
-
- Error: Bitmessage addresses start with BM- Please check %1
+
+ Error: Bitmessage addresses start with BM- Please check {0}
-
- Error: The address %1 is not typed or copied correctly. Please check it.
+
+ Error: The address {0} is not typed or copied correctly. Please check it.
-
- Error: The address %1 contains invalid characters. Please check it.
+
+ Error: The address {0} contains invalid characters. Please check it.
-
- Error: The address version in %1 is too high. Either you need to upgrade your Bitmessage software or your acquaintance is being clever.
+
+ Error: The address version in {0} is too high. Either you need to upgrade your Bitmessage software or your acquaintance is being clever.
-
- Error: Some data encoded in the address %1 is too short. There might be something wrong with the software of your acquaintance.
+
+ Error: Some data encoded in the address {0} is too short. There might be something wrong with the software of your acquaintance.
-
- Error: Some data encoded in the address %1 is too long. There might be something wrong with the software of your acquaintance.
+
+ Error: Some data encoded in the address {0} is too long. There might be something wrong with the software of your acquaintance.
-
- Error: Some data encoded in the address %1 is malformed. There might be something wrong with the software of your acquaintance.
+
+ Error: Some data encoded in the address {0} is malformed. There might be something wrong with the software of your acquaintance.
-
- Error: Something is wrong with the address %1.
+
+ Error: Something is wrong with the address {0}.
@@ -605,8 +605,8 @@ It is important that you back up this file. Would you like to open the file now?
-
- Concerning the address %1, Bitmessage cannot understand address version numbers of %2. Perhaps upgrade Bitmessage to the latest version.
+
+ Concerning the address {0}, Bitmessage cannot understand address version numbers of {1}. Perhaps upgrade Bitmessage to the latest version.
@@ -615,8 +615,8 @@ It is important that you back up this file. Would you like to open the file now?
-
- Concerning the address %1, Bitmessage cannot handle stream numbers of %2. Perhaps upgrade Bitmessage to the latest version.
+
+ Concerning the address {0}, Bitmessage cannot handle stream numbers of {1}. Perhaps upgrade Bitmessage to the latest version.
@@ -750,8 +750,8 @@ It is important that you back up this file. Would you like to open the file now?
-
- Bitmessage cannot find your address %1. Perhaps you removed it?
+
+ Bitmessage cannot find your address {0}. Perhaps you removed it?
@@ -908,8 +908,8 @@ Are you sure you want to delete the channel?
-
- You are using TCP port %1. (This can be changed in the settings).
+
+ You are using TCP port {0}. (This can be changed in the settings).
@@ -1103,8 +1103,8 @@ Are you sure you want to delete the channel?
-
- Zoom level %1%
+
+ Zoom level {0}%
@@ -1118,48 +1118,48 @@ Are you sure you want to delete the channel?
-
- Display the %1 recent broadcast(s) from this address.
+
+ Display the {0} recent broadcast(s) from this address.
-
- New version of PyBitmessage is available: %1. Download it from https://github.com/Bitmessage/PyBitmessage/releases/latest
+
+ New version of PyBitmessage is available: {0}. Download it from https://github.com/Bitmessage/PyBitmessage/releases/latest
-
- Waiting for PoW to finish... %1%
+
+ Waiting for PoW to finish... {0}%
-
- Shutting down Pybitmessage... %1%
+
+ Shutting down Pybitmessage... {0}%
-
- Waiting for objects to be sent... %1%
+
+ Waiting for objects to be sent... {0}%
-
- Saving settings... %1%
+
+ Saving settings... {0}%
-
- Shutting down core... %1%
+
+ Shutting down core... {0}%
-
- Stopping notifications... %1%
+
+ Stopping notifications... {0}%
-
- Shutdown imminent... %1%
+
+ Shutdown imminent... {0}%
@@ -1179,8 +1179,8 @@ Are you sure you want to delete the channel?
-
- Shutting down PyBitmessage... %1%
+
+ Shutting down PyBitmessage... {0}%
@@ -1199,13 +1199,13 @@ Are you sure you want to delete the channel?
-
- Generating %1 new addresses.
+
+ Generating {0} new addresses.
-
- %1 is already in 'Your Identities'. Not adding it again.
+
+ {0} is already in 'Your Identities'. Not adding it again.
@@ -1214,8 +1214,8 @@ Are you sure you want to delete the channel?
-
- SOCKS5 Authentication problem: %1
+
+ SOCKS5 Authentication problem: {0}
@@ -1239,8 +1239,8 @@ Are you sure you want to delete the channel?
-
- Broadcast sent on %1
+
+ Broadcast sent on {0}
@@ -1259,8 +1259,8 @@ Are you sure you want to delete the channel?
-
- Problem: Destination is a mobile device who requests that the destination be included in the message but this is disallowed in your settings. %1
+
+ Problem: Destination is a mobile device who requests that the destination be included in the message but this is disallowed in your settings. {0}
@@ -1272,19 +1272,19 @@ There is no required difficulty for version 2 addresses like this.
+Receiver's required difficulty: {0} and {1}
Doing work necessary to send message.
-Receiver's required difficulty: %1 and %2
+Receiver's required difficulty: {0} and {1}
-
- Problem: The work demanded by the recipient (%1 and %2) is more difficult than you are willing to do. %3
+
+ Problem: The work demanded by the recipient ({0} and {1}) is more difficult than you are willing to do. {2}
-
- 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
+
+ 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. {0}
@@ -1293,8 +1293,8 @@ Receiver's required difficulty: %1 and %2
-
- Message sent. Waiting for acknowledgement. Sent on %1
+
+ Message sent. Waiting for acknowledgement. Sent on {0}
@@ -1308,13 +1308,13 @@ Receiver's required difficulty: %1 and %2
-
- Sending public key request. Waiting for reply. Requested at %1
+
+ Sending public key request. Waiting for reply. Requested at {0}
-
- UPnP port mapping established on port %1
+
+ UPnP port mapping established on port {0}
@@ -1703,28 +1703,28 @@ The 'Random Number' option is selected by default but deterministic ad
-
- Since startup on %1
+
+ Since startup on {0}
-
- Down: %1/s Total: %2
+
+ Down: {0}/s Total: {1}
-
- Up: %1/s Total: %2
+
+ Up: {0}/s Total: {1}
-
- Total Connections: %1
+
+ Total Connections: {0}
-
- Inventory lookups per second: %1
+
+ Inventory lookups per second: {0}
diff --git a/src/translations/bitmessage_en_pirate.qm b/src/translations/bitmessage_en_pirate.qm
index 69e6bde8..753e7e1a 100644
Binary files a/src/translations/bitmessage_en_pirate.qm and b/src/translations/bitmessage_en_pirate.qm differ
diff --git a/src/translations/bitmessage_en_pirate.ts b/src/translations/bitmessage_en_pirate.ts
index 69642a96..48f60e6c 100644
--- a/src/translations/bitmessage_en_pirate.ts
+++ b/src/translations/bitmessage_en_pirate.ts
@@ -241,7 +241,7 @@ Please type the desired email address (including @mailchuck.com) below:
-
+
@@ -261,12 +261,12 @@ Please type the desired email address (including @mailchuck.com) below:
-
+
-
+
@@ -276,7 +276,7 @@ Please type the desired email address (including @mailchuck.com) below:
-
+
@@ -286,17 +286,17 @@ Please type the desired email address (including @mailchuck.com) below:
-
+
-
+
-
+
@@ -306,7 +306,7 @@ Please type the desired email address (including @mailchuck.com) below:
-
+
@@ -347,7 +347,7 @@ Please type the desired email address (including @mailchuck.com) below:
@@ -364,7 +364,7 @@ It is important that you back up this file.
@@ -430,7 +430,7 @@ It is important that you back up this file. Would you like to open the file now?
-
+
@@ -498,52 +498,52 @@ It is important that you back up this file. Would you like to open the file now?
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
@@ -558,7 +558,7 @@ It is important that you back up this file. Would you like to open the file now?
-
+
@@ -568,7 +568,7 @@ It is important that you back up this file. Would you like to open the file now?
-
+
@@ -703,7 +703,7 @@ It is important that you back up this file. Would you like to open the file now?
-
+
@@ -857,7 +857,7 @@ Are you sure you want to delete the channel?
-
+
@@ -1052,7 +1052,7 @@ Are you sure you want to delete the channel?
-
+
@@ -1067,47 +1067,47 @@ Are you sure you want to delete the channel?
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
@@ -1128,7 +1128,7 @@ Are you sure you want to delete the channel?
-
+
@@ -1148,12 +1148,12 @@ Are you sure you want to delete the channel?
-
+
-
+
@@ -1163,7 +1163,7 @@ Are you sure you want to delete the channel?
-
+
@@ -1188,7 +1188,7 @@ Are you sure you want to delete the channel?
-
+
@@ -1208,7 +1208,7 @@ Are you sure you want to delete the channel?
-
+
@@ -1220,17 +1220,17 @@ There is no required difficulty for version 2 addresses like this.
+Receiver's required difficulty: {0} and {1}
-
+
-
+
@@ -1240,7 +1240,7 @@ Receiver's required difficulty: %1 and %2
-
+
@@ -1255,12 +1255,12 @@ Receiver's required difficulty: %1 and %2
-
+
-
+
@@ -1623,27 +1623,27 @@ T' 'Random Number' option be selected by default but deterministi
-
+
-
+
-
+
-
+
-
+
diff --git a/src/translations/bitmessage_eo.qm b/src/translations/bitmessage_eo.qm
index 77c20edf..76fac096 100644
Binary files a/src/translations/bitmessage_eo.qm and b/src/translations/bitmessage_eo.qm differ
diff --git a/src/translations/bitmessage_eo.ts b/src/translations/bitmessage_eo.ts
index 5707a390..835de58d 100644
--- a/src/translations/bitmessage_eo.ts
+++ b/src/translations/bitmessage_eo.ts
@@ -350,8 +350,8 @@ Please type the desired email address (including @mailchuck.com) below:
-
- Iu de viaj adresoj, %1, estas malnova versio 1 adreso. Versioj 1 adresoj ne estas jam subtenataj. Ĉu ni povas forigi ĝin?
+
+ Iu de viaj adresoj, {0}, estas malnova versio 1 adreso. Versioj 1 adresoj ne estas jam subtenataj. Ĉu ni povas forigi ĝin?
@@ -370,13 +370,13 @@ Please type the desired email address (including @mailchuck.com) below:
-
- Mesaĝo sendita. Atendado je konfirmo. Sendita je %1
+
+ Mesaĝo sendita. Atendado je konfirmo. Sendita je {0}
-
- Mesaĝo sendita. Sendita je %1
+
+ Mesaĝo sendita. Sendita je {0}
@@ -385,8 +385,8 @@ Please type the desired email address (including @mailchuck.com) below:
-
- Ricevis konfirmon de la mesaĝo je %1
+
+ Ricevis konfirmon de la mesaĝo je {0}
@@ -395,18 +395,18 @@ Please type the desired email address (including @mailchuck.com) below:
-
- Elsendo je %1
+
+ Elsendo je {0}
-
- Problemo: la demandita laboro de la ricevonto estas pli malfacila ol vi pretas fari. %1
+
+ Problemo: la demandita laboro de la ricevonto estas pli malfacila ol vi pretas fari. {0}
-
- Problemo: la ĉifroŝlosilo de la ricevonto estas rompita. Ne povis ĉifri la mesaĝon. %1
+
+ Problemo: la ĉifroŝlosilo de la ricevonto estas rompita. Ne povis ĉifri la mesaĝon. {0}
@@ -415,8 +415,8 @@ Please type the desired email address (including @mailchuck.com) below:
-
- Nekonata stato: %1 %2
+
+ Nekonata stato: {0} {1}
@@ -456,10 +456,10 @@ Please type the desired email address (including @mailchuck.com) below:
Vi povas administri viajn ŝlosilojn per redakti la dosieron “keys.dat” en la dosierujo
-%1.
+{0}.
Estas grava, ke vi faru sekurkopion de tiu dosiero.
@@ -475,10 +475,10 @@ Estas grava, ke vi faru sekurkopion de tiu dosiero.
Vi povas administri viajn ŝlosilojn per redakti la dosieron “keys.dat” en la dosierujo
-%1.
+{0}.
Estas grava, ke vi faru sekurkopion de tiu dosiero. Ĉu vi volas malfermi la dosieron nun? (Bonvolu certigi ke Bitmesaĝo estas fermita antaŭ fari ŝanĝojn.)
@@ -543,7 +543,7 @@ Estas grava, ke vi faru sekurkopion de tiu dosiero. Ĉu vi volas malfermi la dos
-
+
@@ -611,52 +611,52 @@ Estas grava, ke vi faru sekurkopion de tiu dosiero. Ĉu vi volas malfermi la dos
-
- La mesaĝon kiun vi provis sendi estas tro longa je %1 bitokoj. (La maksimumo estas 261644 bitokoj.) Bonvolu mallongigi ĝin antaŭ sendado.
+
+ La mesaĝon kiun vi provis sendi estas tro longa je {0} bitokoj. (La maksimumo estas 261644 bitokoj.) Bonvolu mallongigi ĝin antaŭ sendado.
-
- Eraro: via konto ne estas registrita je retpoŝta kluzo. Registranta nun kiel %1, bonvolu atendi ĝis la registrado finos antaŭ vi reprovos sendi iun ajn.
+
+ Eraro: via konto ne estas registrita je retpoŝta kluzo. Registranta nun kiel {0}, bonvolu atendi ĝis la registrado finos antaŭ vi reprovos sendi iun ajn.
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
@@ -671,8 +671,8 @@ Estas grava, ke vi faru sekurkopion de tiu dosiero. Ĉu vi volas malfermi la dos
-
- Dum prilaborado de adreso adreso %1, Bitmesaĝo ne povas kompreni numerojn %2 de adresversioj. Eble ĝisdatigu Bitmesaĝon al la plej nova versio.
+
+ Dum prilaborado de adreso adreso {0}, Bitmesaĝo ne povas kompreni numerojn {1} de adresversioj. Eble ĝisdatigu Bitmesaĝon al la plej nova versio.
@@ -681,8 +681,8 @@ Estas grava, ke vi faru sekurkopion de tiu dosiero. Ĉu vi volas malfermi la dos
-
- Dum prilaborado de adreso %1, Bitmesaĝo ne povas priservi %2 fluojn numerojn. Eble ĝisdatigu Bitmesaĝon al la plej nova versio.
+
+ Dum prilaborado de adreso {0}, Bitmesaĝo ne povas priservi {1} fluojn numerojn. Eble ĝisdatigu Bitmesaĝon al la plej nova versio.
@@ -816,8 +816,8 @@ Estas grava, ke vi faru sekurkopion de tiu dosiero. Ĉu vi volas malfermi la dos
-
- Bitmesaĝo ne povas trovi vian adreson %1. Ĉu eble vi forviŝis ĝin?
+
+ Bitmesaĝo ne povas trovi vian adreson {0}. Ĉu eble vi forviŝis ĝin?
@@ -974,7 +974,7 @@ Are you sure you want to delete the channel?
-
+
@@ -1169,8 +1169,8 @@ Are you sure you want to delete the channel?
-
- Pligrandigo: %1
+
+ Pligrandigo: {0}
@@ -1184,48 +1184,48 @@ Are you sure you want to delete the channel?
-
+
-
- La nova versio de PyBitmessage estas disponebla: %1. Elŝutu ĝin de https://github.com/Bitmessage/PyBitmessage/releases/latest
+
+ La nova versio de PyBitmessage estas disponebla: {0}. Elŝutu ĝin de https://github.com/Bitmessage/PyBitmessage/releases/latest
-
- Atendado ĝis laborpruvo finiĝos… %1%
+
+ Atendado ĝis laborpruvo finiĝos… {0}%
-
- Fermado de PyBitmessage… %1%
+
+ Fermado de PyBitmessage… {0}%
-
- Atendado ĝis objektoj estos senditaj… %1%
+
+ Atendado ĝis objektoj estos senditaj… {0}%
-
- Konservado de agordoj… %1%
+
+ Konservado de agordoj… {0}%
-
- Fermado de kerno… %1%
+
+ Fermado de kerno… {0}%
-
- Haltigado de sciigoj… %1%
+
+ Haltigado de sciigoj… {0}%
-
- Fermado tuj… %1%
+
+ Fermado tuj… {0}%
@@ -1239,8 +1239,8 @@ Are you sure you want to delete the channel?
-
- Fermado de PyBitmessage… %1%
+
+ Fermado de PyBitmessage… {0}%
@@ -1259,13 +1259,13 @@ Are you sure you want to delete the channel?
-
- Kreado de %1 novaj adresoj.
+
+ Kreado de {0} novaj adresoj.
-
- %1 jam estas en ‘Viaj Identigoj’. Ĝi ne estos aldonita ree.
+
+ {0} jam estas en ‘Viaj Identigoj’. Ĝi ne estos aldonita ree.
@@ -1274,7 +1274,7 @@ Are you sure you want to delete the channel?
-
+
@@ -1299,8 +1299,8 @@ Are you sure you want to delete the channel?
-
- Elsendo sendita je %1
+
+ Elsendo sendita je {0}
@@ -1319,8 +1319,8 @@ Are you sure you want to delete the channel?
-
- Eraro: celadreso estas portebla aparato kiu necesas, ke la celadreso estu enhavita en la mesaĝo, sed tio estas malpermesita ne viaj agordoj. %1
+
+ Eraro: celadreso estas portebla aparato kiu necesas, ke la celadreso estu enhavita en la mesaĝo, sed tio estas malpermesita ne viaj agordoj. {0}
@@ -1332,19 +1332,19 @@ Malfacilaĵo ne estas bezonata por adresoj versioj 2, kiel tiu ĉi adreso.
+Receiver's required difficulty: {0} and {1}
Kalkulado de laborpruvo, kiu endas por sendi mesaĝon.
-Ricevonto postulas malfacilaĵon: %1 kaj %2
+Ricevonto postulas malfacilaĵon: {0} kaj {1}
-
- Eraro: la demandita laboro de la ricevonto (%1 kaj %2) estas pli malfacila ol vi pretas fari. %3
+
+ Eraro: la demandita laboro de la ricevonto ({0} kaj {1}) estas pli malfacila ol vi pretas fari. {2}
-
- Eraro: Vi provis sendi mesaĝon al vi mem aŭ al kanalo, tamen via ĉifroŝlosilo ne estas trovebla en la dosiero keys.dat. Mesaĝo ne povis esti ĉifrita. %1
+
+ Eraro: Vi provis sendi mesaĝon al vi mem aŭ al kanalo, tamen via ĉifroŝlosilo ne estas trovebla en la dosiero keys.dat. Mesaĝo ne povis esti ĉifrita. {0}
@@ -1353,8 +1353,8 @@ Ricevonto postulas malfacilaĵon: %1 kaj %2
-
- Mesaĝo sendita. Atendado je konfirmo. Sendita je %1
+
+ Mesaĝo sendita. Atendado je konfirmo. Sendita je {0}
@@ -1368,13 +1368,13 @@ Ricevonto postulas malfacilaĵon: %1 kaj %2
-
- Sendado de peto pri publika ĉifroŝlosilo. Atendado je respondo. Petis je %1
+
+ Sendado de peto pri publika ĉifroŝlosilo. Atendado je respondo. Petis je {0}
-
- UPnP pord-mapigo farita je pordo %1
+
+ UPnP pord-mapigo farita je pordo {0}
@@ -1418,18 +1418,18 @@ Ricevonto postulas malfacilaĵon: %1 kaj %2
-
- La nomo %1 ne trovita.
+
+ La nomo {0} ne trovita.
-
- La namecoin-peto fiaskis (%1)
+
+ La namecoin-peto fiaskis ({0})
-
- Nekonata tipo de namecoin-fasado: %1
+
+ Nekonata tipo de namecoin-fasado: {0}
@@ -1438,13 +1438,13 @@ Ricevonto postulas malfacilaĵon: %1 kaj %2
-
- La nomo %1 ne estas atribuita kun bitmesaĝa adreso.
+
+ La nomo {0} ne estas atribuita kun bitmesaĝa adreso.
-
- Sukceso! Namecoind versio %1 funkcias.
+
+ Sukceso! Namecoind versio {0} funkcias.
@@ -1507,53 +1507,53 @@ Bonvenon al facila kaj sekura Bitmesaĝo
-
- Eraro: bitmesaĝaj adresoj komenciĝas kun BM-. Bonvolu kontroli la adreson de ricevonto %1
+
+ Eraro: bitmesaĝaj adresoj komenciĝas kun BM-. Bonvolu kontroli la adreson de ricevonto {0}
-
- Eraro: la adreso de ricevonto %1 estas malprave tajpita aŭ kopiita. Bonvolu kontroli ĝin.
+
+ Eraro: la adreso de ricevonto {0} estas malprave tajpita aŭ kopiita. Bonvolu kontroli ĝin.
-
- Eraro: la adreso de ricevonto %1 enhavas malpermesatajn simbolojn. Bonvolu kontroli ĝin.
+
+ Eraro: la adreso de ricevonto {0} enhavas malpermesatajn simbolojn. Bonvolu kontroli ĝin.
-
- Eraro: la versio de adreso de ricevonto %1 estas tro alta. Eble vi devas ĝisdatigi vian bitmesaĝan programon aŭ via sagaca konato uzas alian programon.
+
+ Eraro: la versio de adreso de ricevonto {0} estas tro alta. Eble vi devas ĝisdatigi vian bitmesaĝan programon aŭ via sagaca konato uzas alian programon.
-
- Eraro: kelkaj datumoj koditaj en la adreso de ricevonto %1 estas tro mallongaj. Povus esti ke io en la programo de via konato malfunkcias.
+
+ Eraro: kelkaj datumoj koditaj en la adreso de ricevonto {0} estas tro mallongaj. Povus esti ke io en la programo de via konato malfunkcias.
-
- Eraro: kelkaj datumoj koditaj en la adreso de ricevonto %1 estas tro longaj. Povus esti ke io en la programo de via konato malfunkcias.
+
+ Eraro: kelkaj datumoj koditaj en la adreso de ricevonto {0} estas tro longaj. Povus esti ke io en la programo de via konato malfunkcias.
-
- Eraro: kelkaj datumoj koditaj en la adreso de ricevonto %1 estas misformitaj. Povus esti ke io en la programo de via konato malfunkcias.
+
+ Eraro: kelkaj datumoj koditaj en la adreso de ricevonto {0} estas misformitaj. Povus esti ke io en la programo de via konato malfunkcias.
-
- Eraro: io malĝustas kun la adreso de ricevonto %1.
+
+ Eraro: io malĝustas kun la adreso de ricevonto {0}.
-
- Eraro: %1
+
+ Eraro: {0}
-
- De %1
+
+ De {0}
@@ -1665,8 +1665,8 @@ Bonvenon al facila kaj sekura Bitmesaĝo
-
- La ligilo "%1" estos malfermita per foliumilo. Tio povas esti malsekura, ĝi povos malanonimigi vin aŭ elŝuti malicajn datumojn. Ĉu vi certas?
+
+ La ligilo "{0}" estos malfermita per foliumilo. Tio povas esti malsekura, ĝi povos malanonimigi vin aŭ elŝuti malicajn datumojn. Ĉu vi certas?
@@ -2001,8 +2001,8 @@ La “hazardnombra” adreso estas antaŭagordita, sed antaŭkalkuleblaj adresoj
-
- Vi uzas TCP-pordon %1 (tio ĉi estas ŝanĝebla en la agordoj).
+
+ Vi uzas TCP-pordon {0} (tio ĉi estas ŝanĝebla en la agordoj).
@@ -2054,28 +2054,28 @@ La “hazardnombra” adreso estas antaŭagordita, sed antaŭkalkuleblaj adresoj
-
- Ekde lanĉo de la programo je %1
+
+ Ekde lanĉo de la programo je {0}
-
- Elŝuto: %1/s Sume: %2
+
+ Elŝuto: {0}/s Sume: {1}
-
- Alŝuto: %1/s Sume: %2
+
+ Alŝuto: {0}/s Sume: {1}
-
- Ĉiuj konektoj: %1
+
+ Ĉiuj konektoj: {0}
-
- Petoj pri inventaro en sekundo: %1
+
+ Petoj pri inventaro en sekundo: {0}
@@ -2240,8 +2240,8 @@ La “hazardnombra” adreso estas antaŭagordita, sed antaŭkalkuleblaj adresoj
newchandialog
-
- Sukcese kreis / anigis al la kanalo %1
+
+ Sukcese kreis / anigis al la kanalo {0}
diff --git a/src/translations/bitmessage_fr.qm b/src/translations/bitmessage_fr.qm
index 8cb08a3a..d54bf164 100644
Binary files a/src/translations/bitmessage_fr.qm and b/src/translations/bitmessage_fr.qm differ
diff --git a/src/translations/bitmessage_fr.ts b/src/translations/bitmessage_fr.ts
index 149fd1ef..44fa0248 100644
--- a/src/translations/bitmessage_fr.ts
+++ b/src/translations/bitmessage_fr.ts
@@ -314,8 +314,8 @@ Please type the desired email address (including @mailchuck.com) below:
-
- Une de vos adresses, %1, est une vieille adresse de la version 1. Les adresses de la version 1 ne sont plus supportées. Nous pourrions la supprimer maintenant?
+
+ Une de vos adresses, {0}, est une vieille adresse de la version 1. Les adresses de la version 1 ne sont plus supportées. Nous pourrions la supprimer maintenant?
@@ -334,13 +334,13 @@ Please type the desired email address (including @mailchuck.com) below:
-
- Message envoyé. En attente de l’accusé de réception. Envoyé %1
+
+ Message envoyé. En attente de l’accusé de réception. Envoyé {0}
-
- Message envoyé. Envoyé %1
+
+ Message envoyé. Envoyé {0}
@@ -349,8 +349,8 @@ Please type the desired email address (including @mailchuck.com) below:
-
- Accusé de réception reçu %1
+
+ Accusé de réception reçu {0}
@@ -359,18 +359,18 @@ Please type the desired email address (including @mailchuck.com) below:
-
- Message de diffusion du %1
+
+ Message de diffusion du {0}
-
- Problème : Le travail demandé par le destinataire est plus difficile que ce que vous avez paramétré. %1
+
+ Problème : Le travail demandé par le destinataire est plus difficile que ce que vous avez paramétré. {0}
-
- Problème : la clé de chiffrement du destinataire n’est pas bonne. Il n’a pas été possible de chiffrer le message. %1
+
+ Problème : la clé de chiffrement du destinataire n’est pas bonne. Il n’a pas été possible de chiffrer le message. {0}
@@ -379,8 +379,8 @@ Please type the desired email address (including @mailchuck.com) below:
-
- Statut inconnu : %1 %2
+
+ Statut inconnu : {0} {1}
@@ -420,9 +420,9 @@ Please type the desired email address (including @mailchuck.com) below:
- Vous pouvez éditer vos clés en éditant le fichier keys.dat stocké dans le répertoire %1.
+ Vous pouvez éditer vos clés en éditant le fichier keys.dat stocké dans le répertoire {0}.
Il est important de faire des sauvegardes de ce fichier.
@@ -438,9 +438,9 @@ Il est important de faire des sauvegardes de ce fichier.
- Vous pouvez éditer vos clés en éditant le fichier keys.dat stocké dans le répertoire %1. Il est important de faire des sauvegardes de ce fichier. Souhaitez-vous l’ouvrir maintenant? (Assurez-vous de fermer Bitmessage avant d’effectuer des changements.)
+ Vous pouvez éditer vos clés en éditant le fichier keys.dat stocké dans le répertoire {0}. Il est important de faire des sauvegardes de ce fichier. Souhaitez-vous l’ouvrir maintenant? (Assurez-vous de fermer Bitmessage avant d’effectuer des changements.)
@@ -504,7 +504,7 @@ It is important that you back up this file. Would you like to open the file now?
-
+
@@ -573,52 +573,52 @@ Le destinataire doit l’obtenir avant ce temps. Si votre client Bitmessage ne r
-
- Le message que vous essayez d’envoyer est trop long de %1 octets (le maximum est 261644 octets). Veuillez le réduire avant de l’envoyer.
+
+ Le message que vous essayez d’envoyer est trop long de {0} octets (le maximum est 261644 octets). Veuillez le réduire avant de l’envoyer.
-
- Erreur : votre compte n’a pas été inscrit à une passerelle de courrier électronique. Envoi de l’inscription maintenant en tant que %1, veuillez patienter tandis que l’inscription est en cours de traitement, avant de retenter l’envoi.
+
+ Erreur : votre compte n’a pas été inscrit à une passerelle de courrier électronique. Envoi de l’inscription maintenant en tant que {0}, veuillez patienter tandis que l’inscription est en cours de traitement, avant de retenter l’envoi.
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
@@ -633,8 +633,8 @@ Le destinataire doit l’obtenir avant ce temps. Si votre client Bitmessage ne r
-
- Concernant l’adresse %1, Bitmessage ne peut pas comprendre les numéros de version de %2. Essayez de mettre à jour Bitmessage vers la dernière version.
+
+ Concernant l’adresse {0}, Bitmessage ne peut pas comprendre les numéros de version de {1}. Essayez de mettre à jour Bitmessage vers la dernière version.
@@ -643,8 +643,8 @@ Le destinataire doit l’obtenir avant ce temps. Si votre client Bitmessage ne r
-
- Concernant l’adresse %1, Bitmessage ne peut pas supporter les nombres de flux de %2. Essayez de mettre à jour Bitmessage vers la dernière version.
+
+ Concernant l’adresse {0}, Bitmessage ne peut pas supporter les nombres de flux de {1}. Essayez de mettre à jour Bitmessage vers la dernière version.
@@ -778,8 +778,8 @@ Le destinataire doit l’obtenir avant ce temps. Si votre client Bitmessage ne r
-
- Bitmessage ne peut pas trouver votre adresse %1. Peut-être l’avez-vous supprimée?
+
+ Bitmessage ne peut pas trouver votre adresse {0}. Peut-être l’avez-vous supprimée?
@@ -936,7 +936,7 @@ Are you sure you want to delete the channel?
-
+
@@ -1131,8 +1131,8 @@ Are you sure you want to delete the channel?
-
- Niveau de zoom %1%
+
+ Niveau de zoom {0}%
@@ -1146,48 +1146,48 @@ Are you sure you want to delete the channel?
-
+
-
- Une nouvelle version de PyBitmessage est disponible : %1. Veuillez la télécharger depuis https://github.com/Bitmessage/PyBitmessage/releases/latest
+
+ Une nouvelle version de PyBitmessage est disponible : {0}. Veuillez la télécharger depuis https://github.com/Bitmessage/PyBitmessage/releases/latest
-
- En attente de la fin de la PoW… %1%
+
+ En attente de la fin de la PoW… {0}%
-
- Pybitmessage en cours d’arrêt… %1%
+
+ Pybitmessage en cours d’arrêt… {0}%
-
- En attente de l’envoi des objets… %1%
+
+ En attente de l’envoi des objets… {0}%
-
- Enregistrement des paramètres… %1%
+
+ Enregistrement des paramètres… {0}%
-
- Cœur en cours d’arrêt… %1%
+
+ Cœur en cours d’arrêt… {0}%
-
- Arrêt des notifications… %1%
+
+ Arrêt des notifications… {0}%
-
- Arrêt imminent… %1%
+
+ Arrêt imminent… {0}%
@@ -1201,8 +1201,8 @@ Are you sure you want to delete the channel?
-
- PyBitmessage en cours d’arrêt… %1%
+
+ PyBitmessage en cours d’arrêt… {0}%
@@ -1221,13 +1221,13 @@ Are you sure you want to delete the channel?
-
- Production de %1 nouvelles adresses.
+
+ Production de {0} nouvelles adresses.
-
- %1 est déjà dans "Vos identités". Il ne sera pas ajouté de nouveau.
+
+ {0} est déjà dans "Vos identités". Il ne sera pas ajouté de nouveau.
@@ -1236,7 +1236,7 @@ Are you sure you want to delete the channel?
-
+
@@ -1261,8 +1261,8 @@ Are you sure you want to delete the channel?
-
- Message de diffusion envoyé %1
+
+ Message de diffusion envoyé {0}
@@ -1281,8 +1281,8 @@ Are you sure you want to delete the channel?
-
- Problème : la destination est un dispositif mobile qui nécessite que la destination soit incluse dans le message mais ceci n’est pas autorisé dans vos paramètres. %1
+
+ Problème : la destination est un dispositif mobile qui nécessite que la destination soit incluse dans le message mais ceci n’est pas autorisé dans vos paramètres. {0}
@@ -1294,19 +1294,19 @@ Il n’y a pas de difficulté requise pour les adresses version 2 comme celle-ci
+Receiver's required difficulty: {0} and {1}
Travail en cours afin d’envoyer le message.
-Difficulté requise du destinataire : %1 et %2
+Difficulté requise du destinataire : {0} et {1}
-
- Problème : Le travail demandé par le destinataire (%1 and %2) est plus difficile que ce que vous avez paramétré. %3
+
+ Problème : Le travail demandé par le destinataire ({0} and {1}) est plus difficile que ce que vous avez paramétré. {2}
-
- Problème : Vous essayez d’envoyer un message à un canal ou à vous-même mais votre clef de chiffrement n’a pas été trouvée dans le fichier keys.dat. Le message ne peut pas être chiffré. %1
+
+ Problème : Vous essayez d’envoyer un message à un canal ou à vous-même mais votre clef de chiffrement n’a pas été trouvée dans le fichier keys.dat. Le message ne peut pas être chiffré. {0}
@@ -1315,8 +1315,8 @@ Difficulté requise du destinataire : %1 et %2
-
- Message envoyé. En attente de l’accusé de réception. Envoyé %1
+
+ Message envoyé. En attente de l’accusé de réception. Envoyé {0}
@@ -1330,13 +1330,13 @@ Difficulté requise du destinataire : %1 et %2
-
- Envoi d’une demande de clef publique. En attente d’une réponse. Demandée à %1
+
+ Envoi d’une demande de clef publique. En attente d’une réponse. Demandée à {0}
-
- Transfert de port UPnP établi sur le port %1
+
+ Transfert de port UPnP établi sur le port {0}
@@ -1380,13 +1380,13 @@ Difficulté requise du destinataire : %1 et %2
-
- Le nom %1 n'a pas été trouvé.
+
+ Le nom {0} n'a pas été trouvé.
-
- La requête Namecoin a échouée (%1)
+
+ La requête Namecoin a échouée ({0})
@@ -1395,18 +1395,18 @@ Difficulté requise du destinataire : %1 et %2
-
- Le nom %1 n'a aucune donnée JSON valide.
+
+ Le nom {0} n'a aucune donnée JSON valide.
-
- Le nom %1 n'a aucune adresse Bitmessage d'associée.
+
+ Le nom {0} n'a aucune adresse Bitmessage d'associée.
-
- Succès ! Namecoind version %1 en cours d'exécution.
+
+ Succès ! Namecoind version {0} en cours d'exécution.
@@ -1470,53 +1470,53 @@ Bienvenue dans le facile et sécurisé Bitmessage
-
- Erreur : Les adresses Bitmessage commencent par BM- Veuillez vérifier l'adresse du destinataire %1
+
+ Erreur : Les adresses Bitmessage commencent par BM- Veuillez vérifier l'adresse du destinataire {0}
-
- Erreur : L’adresse du destinataire %1 n’est pas correctement tapée ou recopiée. Veuillez la vérifier.
+
+ Erreur : L’adresse du destinataire {0} n’est pas correctement tapée ou recopiée. Veuillez la vérifier.
-
- Erreur : L’adresse du destinataire %1 contient des caractères invalides. Veuillez la vérifier.
+
+ Erreur : L’adresse du destinataire {0} contient des caractères invalides. Veuillez la vérifier.
-
- Erreur : la version de l’adresse destinataire %1 est trop élevée. Vous devez mettre à niveau votre logiciel Bitmessage ou alors celui de votre connaissance est plus intelligent.
+
+ Erreur : la version de l’adresse destinataire {0} est trop élevée. Vous devez mettre à niveau votre logiciel Bitmessage ou alors celui de votre connaissance est plus intelligent.
-
- Erreur : quelques données codées dans l’adresse destinataire %1 sont trop courtes. Il pourrait y avoir un soucis avec le logiciel de votre connaissance.
+
+ Erreur : quelques données codées dans l’adresse destinataire {0} sont trop courtes. Il pourrait y avoir un soucis avec le logiciel de votre connaissance.
-
- Erreur : quelques données codées dans l’adresse destinataire %1 sont trop longues. Il pourrait y avoir un soucis avec le logiciel de votre connaissance.
+
+ Erreur : quelques données codées dans l’adresse destinataire {0} sont trop longues. Il pourrait y avoir un soucis avec le logiciel de votre connaissance.
-
- Erreur : quelques données codées dans l’adresse destinataire %1 sont mal formées. Il pourrait y avoir un soucis avec le logiciel de votre connaissance.
+
+ Erreur : quelques données codées dans l’adresse destinataire {0} sont mal formées. Il pourrait y avoir un soucis avec le logiciel de votre connaissance.
-
- Erreur : quelque chose ne va pas avec l'adresse de destinataire %1.
+
+ Erreur : quelque chose ne va pas avec l'adresse de destinataire {0}.
-
- Erreur : %1
+
+ Erreur : {0}
-
- De %1
+
+ De {0}
@@ -1628,8 +1628,8 @@ Bienvenue dans le facile et sécurisé Bitmessage
-
- Le lien "%1" s'ouvrira dans un navigateur. Cela pourrait être un risque de sécurité, cela pourrait vous désanonymiser ou télécharger des données malveillantes. Êtes-vous sûr(e) ?
+
+ Le lien "{0}" s'ouvrira dans un navigateur. Cela pourrait être un risque de sécurité, cela pourrait vous désanonymiser ou télécharger des données malveillantes. Êtes-vous sûr(e) ?
@@ -1964,8 +1964,8 @@ L’option "Nombre Aléatoire" est sélectionnée par défaut mais les
-
- Vous utilisez le port TCP %1. (Ceci peut être changé dans les paramètres).
+
+ Vous utilisez le port TCP {0}. (Ceci peut être changé dans les paramètres).
@@ -2017,28 +2017,28 @@ L’option "Nombre Aléatoire" est sélectionnée par défaut mais les
-
- Démarré depuis le %1
+
+ Démarré depuis le {0}
-
- Téléchargées : %1/s Total : %2
+
+ Téléchargées : {0}/s Total : {1}
-
- Téléversées : %1/s Total : %2
+
+ Téléversées : {0}/s Total : {1}
-
- Total des connexions : %1
+
+ Total des connexions : {0}
-
- Consultations d’inventaire par seconde : %1
+
+ Consultations d’inventaire par seconde : {0}
@@ -2203,8 +2203,8 @@ L’option "Nombre Aléatoire" est sélectionnée par défaut mais les
newchandialog
-
- Le canal %1 a été rejoint ou créé avec succès.
+
+ Le canal {0} a été rejoint ou créé avec succès.
diff --git a/src/translations/bitmessage_it.qm b/src/translations/bitmessage_it.qm
index d38e68bc..8b2e9915 100644
Binary files a/src/translations/bitmessage_it.qm and b/src/translations/bitmessage_it.qm differ
diff --git a/src/translations/bitmessage_it.ts b/src/translations/bitmessage_it.ts
index dbefce30..229e02fa 100644
--- a/src/translations/bitmessage_it.ts
+++ b/src/translations/bitmessage_it.ts
@@ -279,8 +279,8 @@ Il gateway email non condurrà operazioni PGP a vostro nome. È possibile
-
- Uno dei tuoi indirizzi, %1, è un indirizzo vecchio versione 1. Gli indirizzi versione 1 non sono più supportati. Posso eliminarlo ora?
+
+ Uno dei tuoi indirizzi, {0}, è un indirizzo vecchio versione 1. Gli indirizzi versione 1 non sono più supportati. Posso eliminarlo ora?
@@ -299,13 +299,13 @@ Il gateway email non condurrà operazioni PGP a vostro nome. È possibile
-
+
-
- Messaggio inviato. Inviato a %1
+
+ Messaggio inviato. Inviato a {0}
@@ -314,7 +314,7 @@ Il gateway email non condurrà operazioni PGP a vostro nome. È possibile
-
+
@@ -324,17 +324,17 @@ Il gateway email non condurrà operazioni PGP a vostro nome. È possibile
-
+
-
+
-
+
@@ -344,7 +344,7 @@ Il gateway email non condurrà operazioni PGP a vostro nome. È possibile
-
+
@@ -385,7 +385,7 @@ Il gateway email non condurrà operazioni PGP a vostro nome. È possibile
@@ -402,7 +402,7 @@ It is important that you back up this file.
@@ -468,7 +468,7 @@ It is important that you back up this file. Would you like to open the file now?
-
+
@@ -536,52 +536,52 @@ It is important that you back up this file. Would you like to open the file now?
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
@@ -596,7 +596,7 @@ It is important that you back up this file. Would you like to open the file now?
-
+
@@ -606,7 +606,7 @@ It is important that you back up this file. Would you like to open the file now?
-
+
@@ -741,7 +741,7 @@ It is important that you back up this file. Would you like to open the file now?
-
+
@@ -895,7 +895,7 @@ Are you sure you want to delete the channel?
-
+
@@ -1090,7 +1090,7 @@ Are you sure you want to delete the channel?
-
+
@@ -1105,47 +1105,47 @@ Are you sure you want to delete the channel?
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
@@ -1166,7 +1166,7 @@ Are you sure you want to delete the channel?
-
+
@@ -1186,12 +1186,12 @@ Are you sure you want to delete the channel?
-
+
-
+
@@ -1201,7 +1201,7 @@ Are you sure you want to delete the channel?
-
+
@@ -1226,7 +1226,7 @@ Are you sure you want to delete the channel?
-
+
@@ -1246,7 +1246,7 @@ Are you sure you want to delete the channel?
-
+
@@ -1258,17 +1258,17 @@ There is no required difficulty for version 2 addresses like this.
+Receiver's required difficulty: {0} and {1}
-
+
-
+
@@ -1278,7 +1278,7 @@ Receiver's required difficulty: %1 and %2
-
+
@@ -1293,12 +1293,12 @@ Receiver's required difficulty: %1 and %2
-
+
-
+
@@ -1660,27 +1660,27 @@ The 'Random Number' option is selected by default but deterministic ad
-
+
-
+
-
+
-
- Connessioni totali: %1
+
+ Connessioni totali: {0}
-
+
diff --git a/src/translations/bitmessage_ja.qm b/src/translations/bitmessage_ja.qm
index 77fa63d1..8637aa2e 100644
Binary files a/src/translations/bitmessage_ja.qm and b/src/translations/bitmessage_ja.qm differ
diff --git a/src/translations/bitmessage_ja.ts b/src/translations/bitmessage_ja.ts
index f11289f5..2b1ebe97 100644
--- a/src/translations/bitmessage_ja.ts
+++ b/src/translations/bitmessage_ja.ts
@@ -352,8 +352,8 @@ Please type the desired email address (including @mailchuck.com) below:
-
- %1は古いバージョン1のアドレスです。バージョン1のアドレスはサポートが終了しています。すぐに削除しますか?
+
+ {0}は古いバージョン1のアドレスです。バージョン1のアドレスはサポートが終了しています。すぐに削除しますか?
@@ -372,13 +372,13 @@ Please type the desired email address (including @mailchuck.com) below:
-
- メッセージを送信しました。 確認応答を待っています。 %1 で送信されました
+
+ メッセージを送信しました。 確認応答を待っています。 {0} で送信されました
-
- メッセージは送信されました。送信先: %1
+
+ メッセージは送信されました。送信先: {0}
@@ -387,8 +387,8 @@ Please type the desired email address (including @mailchuck.com) below:
-
- メッセージの確認を受け取りました %1
+
+ メッセージの確認を受け取りました {0}
@@ -397,18 +397,18 @@ Please type the desired email address (including @mailchuck.com) below:
-
- 配信: %1
+
+ 配信: {0}
-
- 問題: 受信者が要求している処理は現在あなたが設定しているよりも高い難易度です。 %1
+
+ 問題: 受信者が要求している処理は現在あなたが設定しているよりも高い難易度です。 {0}
-
- 問題: 受信者の暗号鍵は正当でない物です。メッセージを暗号化できません。 %1
+
+ 問題: 受信者の暗号鍵は正当でない物です。メッセージを暗号化できません。 {0}
@@ -417,8 +417,8 @@ Please type the desired email address (including @mailchuck.com) below:
-
- 不明なステータス: %1 %2
+
+ 不明なステータス: {0} {1}
@@ -458,9 +458,9 @@ Please type the desired email address (including @mailchuck.com) below:
- %1
+ {0}
に保存されているkeys.datファイルを編集することで鍵を管理できます。
このファイルをバックアップしておくことは重要です。
@@ -477,9 +477,9 @@ It is important that you back up this file.
- %1
+ {0}
に保存されているkeys.datファイルを編集することで鍵を管理できます。
ファイルをバックアップしておくことは重要です。すぐにファイルを開きますか?(必ず編集する前にBitmessageを終了してください)
@@ -545,7 +545,7 @@ It is important that you back up this file. Would you like to open the file now?
-
+
@@ -616,52 +616,52 @@ It is important that you back up this file. Would you like to open the file now?
-
- 送信しようとしているメッセージが %1 バイト長すぎます。 (最大は261644バイトです)。 送信する前に短くしてください。
+
+ 送信しようとしているメッセージが {0} バイト長すぎます。 (最大は261644バイトです)。 送信する前に短くしてください。
-
- エラー: アカウントがメールゲートウェイに登録されていません。 今 %1 として登録を送信しています。送信を再試行する前に、登録が処理されるのをお待ちください。
+
+ エラー: アカウントがメールゲートウェイに登録されていません。 今 {0} として登録を送信しています。送信を再試行する前に、登録が処理されるのをお待ちください。
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
@@ -676,8 +676,8 @@ It is important that you back up this file. Would you like to open the file now?
-
- アドレス %1 に接続しています。%2 のバージョン番号は処理できません。Bitmessageを最新のバージョンへアップデートしてください。
+
+ アドレス {0} に接続しています。{1} のバージョン番号は処理できません。Bitmessageを最新のバージョンへアップデートしてください。
@@ -686,8 +686,8 @@ It is important that you back up this file. Would you like to open the file now?
-
- アドレス %1 に接続しています。%2 のストリーム番号は処理できません。Bitmessageを最新のバージョンへアップデートしてください。
+
+ アドレス {0} に接続しています。{1} のストリーム番号は処理できません。Bitmessageを最新のバージョンへアップデートしてください。
@@ -821,8 +821,8 @@ It is important that you back up this file. Would you like to open the file now?
-
- アドレス %1 が見つかりません。既に削除していませんか?
+
+ アドレス {0} が見つかりません。既に削除していませんか?
@@ -979,7 +979,7 @@ Are you sure you want to delete the channel?
-
+
@@ -1174,8 +1174,8 @@ Are you sure you want to delete the channel?
-
- ズーム レベル %1%
+
+ ズーム レベル {0}%
@@ -1189,48 +1189,48 @@ Are you sure you want to delete the channel?
-
+
-
- 新しいバージョンの PyBitmessage が利用可能です: %1。 https://github.com/Bitmessage/PyBitmessage/releases/latest からダウンロードしてください
+
+ 新しいバージョンの PyBitmessage が利用可能です: {0}。 https://github.com/Bitmessage/PyBitmessage/releases/latest からダウンロードしてください
-
- PoW(プルーフオブワーク)が完了するのを待っています... %1%
+
+ PoW(プルーフオブワーク)が完了するのを待っています... {0}%
-
- Pybitmessageをシャットダウンしています... %1%
+
+ Pybitmessageをシャットダウンしています... {0}%
-
- オブジェクトの送信待ち... %1%
+
+ オブジェクトの送信待ち... {0}%
-
- 設定を保存しています... %1%
+
+ 設定を保存しています... {0}%
-
- コアをシャットダウンしています... %1%
+
+ コアをシャットダウンしています... {0}%
-
- 通知を停止しています... %1%
+
+ 通知を停止しています... {0}%
-
- すぐにシャットダウンします... %1%
+
+ すぐにシャットダウンします... {0}%
@@ -1244,8 +1244,8 @@ Are you sure you want to delete the channel?
-
- PyBitmessageをシャットダウンしています... %1%
+
+ PyBitmessageをシャットダウンしています... {0}%
@@ -1264,13 +1264,13 @@ Are you sure you want to delete the channel?
-
- %1 の新しいアドレスを生成しています。
+
+ {0} の新しいアドレスを生成しています。
-
- %1はすでに「アドレス一覧」にあります。 もう一度追加できません。
+
+ {0}はすでに「アドレス一覧」にあります。 もう一度追加できません。
@@ -1279,7 +1279,7 @@ Are you sure you want to delete the channel?
-
+
@@ -1304,8 +1304,8 @@ Are you sure you want to delete the channel?
-
- 配信が送信されました %1
+
+ 配信が送信されました {0}
@@ -1324,8 +1324,8 @@ Are you sure you want to delete the channel?
-
- 問題: メッセージに含まれた宛先のリクエストはモバイルデバイスですが、設定では許可されていません。 %1
+
+ 問題: メッセージに含まれた宛先のリクエストはモバイルデバイスですが、設定では許可されていません。 {0}
@@ -1337,18 +1337,18 @@ There is no required difficulty for version 2 addresses like this.
+Receiver's required difficulty: {0} and {1}
メッセージの送信に必要な処理を行っています。
-受信者の必要な難易度: %1 および %2
+受信者の必要な難易度: {0} および {1}
-
- 問題: 受信者が要求している処理 (%1 および %2) は、現在あなたが設定しているよりも高い難易度です。 %3
+
+ 問題: 受信者が要求している処理 ({0} および {1}) は、現在あなたが設定しているよりも高い難易度です。 {2}
-
+
問題: あなた自身またはチャンネルにメッセージを送信しようとしていますが、暗号鍵がkeys.datファイルに見つかりませんでした。 メッセージを暗号化できませんでした。 %1
@@ -1358,8 +1358,8 @@ Receiver's required difficulty: %1 and %2
-
- メッセージを送信しました。 確認応答を待っています。 %1 で送信しました
+
+ メッセージを送信しました。 確認応答を待っています。 {0} で送信しました
@@ -1373,13 +1373,13 @@ Receiver's required difficulty: %1 and %2
-
- 公開鍵のリクエストを送信しています。 返信を待っています。 %1 でリクエストしました
+
+ 公開鍵のリクエストを送信しています。 返信を待っています。 {0} でリクエストしました
-
- ポート%1でUPnPポートマッピングが確立しました
+
+ ポート{0}でUPnPポートマッピングが確立しました
@@ -1423,18 +1423,18 @@ Receiver's required difficulty: %1 and %2
-
- 名前 %1 が見つかりませんでした。
+
+ 名前 {0} が見つかりませんでした。
-
- namecoin のクエリに失敗しました (%1)
+
+ namecoin のクエリに失敗しました ({0})
-
- 不明な namecoin インターフェースタイプ: %1
+
+ 不明な namecoin インターフェースタイプ: {0}
@@ -1443,13 +1443,13 @@ Receiver's required difficulty: %1 and %2
-
- 名前 %1 は関連付けられた Bitmessage アドレスがありません。
+
+ 名前 {0} は関連付けられた Bitmessage アドレスがありません。
-
- 成功! Namecoind バージョン %1 が実行中。
+
+ 成功! Namecoind バージョン {0} が実行中。
@@ -1513,53 +1513,53 @@ Receiver's required difficulty: %1 and %2
-
- エラー: BitmessageのアドレスはBM-で始まります。 受信者のアドレス %1 を確認してください
+
+ エラー: BitmessageのアドレスはBM-で始まります。 受信者のアドレス {0} を確認してください
-
- エラー: 受信者のアドレス %1 は正しく入力、またはコピーされていません。確認して下さい。
+
+ エラー: 受信者のアドレス {0} は正しく入力、またはコピーされていません。確認して下さい。
-
- エラー: 受信者のアドレス %1 は不正な文字を含んでいます。確認して下さい。
+
+ エラー: 受信者のアドレス {0} は不正な文字を含んでいます。確認して下さい。
-
- エラー: 受信者アドレスのバージョン %1 は高すぎます。 Bitmessageソフトウェアをアップグレードする必要があるか、連絡先が賢明になっているかのいずれかです。
+
+ エラー: 受信者アドレスのバージョン {0} は高すぎます。 Bitmessageソフトウェアをアップグレードする必要があるか、連絡先が賢明になっているかのいずれかです。
-
- エラー: アドレス %1 でエンコードされたデータが短すぎます。連絡先のソフトウェアが何かしら誤っている可能性があります。
+
+ エラー: アドレス {0} でエンコードされたデータが短すぎます。連絡先のソフトウェアが何かしら誤っている可能性があります。
-
- エラー: 受信者のアドレス %1 でエンコードされたデータが短すぎます。連絡先のソフトウェアが何かしら誤っている可能性があります。
+
+ エラー: 受信者のアドレス {0} でエンコードされたデータが短すぎます。連絡先のソフトウェアが何かしら誤っている可能性があります。
-
- エラー: 受信者のアドレス %1 でエンコードされたデータの一部が不正です。連絡先のソフトウェアが何かしら誤っている可能性があります。
+
+ エラー: 受信者のアドレス {0} でエンコードされたデータの一部が不正です。連絡先のソフトウェアが何かしら誤っている可能性があります。
-
- エラー: 受信者のアドレス %1 には何かしら誤りがあります。
+
+ エラー: 受信者のアドレス {0} には何かしら誤りがあります。
-
- エラー: %1
+
+ エラー: {0}
-
- 送信元 %1
+
+ 送信元 {0}
@@ -1671,8 +1671,8 @@ Receiver's required difficulty: %1 and %2
-
- リンク "%1" はブラウザで開きます。 セキュリティリスクの可能性があります。匿名性がなくなったり、悪意のあるデータをダウンロードする可能性があります。 よろしいですか?
+
+ リンク "{0}" はブラウザで開きます。 セキュリティリスクの可能性があります。匿名性がなくなったり、悪意のあるデータをダウンロードする可能性があります。 よろしいですか?
@@ -2006,8 +2006,8 @@ The 'Random Number' option is selected by default but deterministic ad
-
- 使用中のポート %1 (設定で変更できます)。
+
+ 使用中のポート {0} (設定で変更できます)。
@@ -2059,28 +2059,28 @@ The 'Random Number' option is selected by default but deterministic ad
-
- 起動日時 %1
+
+ 起動日時 {0}
-
- ダウン: %1/秒 合計: %2
+
+ ダウン: {0}/秒 合計: {1}
-
- アップ: %1/秒 合計: %2
+
+ アップ: {0}/秒 合計: {1}
-
- 接続数: %1
+
+ 接続数: {0}
-
- 毎秒のインベントリ検索: %1
+
+ 毎秒のインベントリ検索: {0}
@@ -2245,8 +2245,8 @@ The 'Random Number' option is selected by default but deterministic ad
newchandialog
-
- チャンネル %1 を正常に作成 / 参加しました
+
+ チャンネル {0} を正常に作成 / 参加しました
diff --git a/src/translations/bitmessage_nb.ts b/src/translations/bitmessage_nb.ts
index 21f641c0..0ee74d6a 100644
--- a/src/translations/bitmessage_nb.ts
+++ b/src/translations/bitmessage_nb.ts
@@ -240,7 +240,7 @@ Please type the desired email address (including @mailchuck.com) below:
-
+
@@ -250,12 +250,12 @@ Please type the desired email address (including @mailchuck.com) below:
-
+
-
+
@@ -275,12 +275,12 @@ Please type the desired email address (including @mailchuck.com) below:
-
+
-
+
@@ -290,7 +290,7 @@ Please type the desired email address (including @mailchuck.com) below:
-
+
@@ -300,17 +300,17 @@ Please type the desired email address (including @mailchuck.com) below:
-
+
-
+
-
+
@@ -320,7 +320,7 @@ Please type the desired email address (including @mailchuck.com) below:
-
+
@@ -361,7 +361,7 @@ Please type the desired email address (including @mailchuck.com) below:
@@ -378,7 +378,7 @@ It is important that you back up this file.
@@ -444,7 +444,7 @@ It is important that you back up this file. Would you like to open the file now?
-
+
@@ -512,52 +512,52 @@ It is important that you back up this file. Would you like to open the file now?
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
@@ -572,7 +572,7 @@ It is important that you back up this file. Would you like to open the file now?
-
+
@@ -582,7 +582,7 @@ It is important that you back up this file. Would you like to open the file now?
-
+
@@ -722,7 +722,7 @@ It is important that you back up this file. Would you like to open the file now?
-
+
@@ -876,7 +876,7 @@ Are you sure you want to delete the channel?
-
+
@@ -1081,7 +1081,7 @@ Are you sure you want to delete the channel?
-
+
@@ -1096,7 +1096,7 @@ Are you sure you want to delete the channel?
-
+
@@ -1449,47 +1449,47 @@ The 'Random Number' option is selected by default but deterministic ad
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
diff --git a/src/translations/bitmessage_nl.qm b/src/translations/bitmessage_nl.qm
index 72a7ade2..94ba1524 100644
Binary files a/src/translations/bitmessage_nl.qm and b/src/translations/bitmessage_nl.qm differ
diff --git a/src/translations/bitmessage_nl.ts b/src/translations/bitmessage_nl.ts
index 3e7c1640..dd77549e 100644
--- a/src/translations/bitmessage_nl.ts
+++ b/src/translations/bitmessage_nl.ts
@@ -242,7 +242,7 @@ Voer het gewenste e-mail adres (inclusief @mailchuck.com) hieronder in:
-
+
@@ -262,13 +262,13 @@ Voer het gewenste e-mail adres (inclusief @mailchuck.com) hieronder in:
-
- Bericht verzonden. Wachten op bevestiging. Verzonden op %1
+
+ Bericht verzonden. Wachten op bevestiging. Verzonden op {0}
-
- Bericht verzonden. Verzonden op %1
+
+ Bericht verzonden. Verzonden op {0}
@@ -277,8 +277,8 @@ Voer het gewenste e-mail adres (inclusief @mailchuck.com) hieronder in:
-
- Bevestiging van het bericht ontvangen op %1
+
+ Bevestiging van het bericht ontvangen op {0}
@@ -287,17 +287,17 @@ Voer het gewenste e-mail adres (inclusief @mailchuck.com) hieronder in:
-
+
-
+
-
+
@@ -307,8 +307,8 @@ Voer het gewenste e-mail adres (inclusief @mailchuck.com) hieronder in:
-
- Status onbekend: %1 %2
+
+ Status onbekend: {0} {1}
@@ -348,7 +348,7 @@ Voer het gewenste e-mail adres (inclusief @mailchuck.com) hieronder in:
@@ -365,7 +365,7 @@ It is important that you back up this file.
@@ -431,7 +431,7 @@ It is important that you back up this file. Would you like to open the file now?
-
+
@@ -499,52 +499,52 @@ It is important that you back up this file. Would you like to open the file now?
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
@@ -559,7 +559,7 @@ It is important that you back up this file. Would you like to open the file now?
-
+
@@ -569,7 +569,7 @@ It is important that you back up this file. Would you like to open the file now?
-
+
@@ -704,7 +704,7 @@ It is important that you back up this file. Would you like to open the file now?
-
+
@@ -858,7 +858,7 @@ Are you sure you want to delete the channel?
-
+
@@ -1053,7 +1053,7 @@ Are you sure you want to delete the channel?
-
+
@@ -1068,47 +1068,47 @@ Are you sure you want to delete the channel?
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
@@ -1129,7 +1129,7 @@ Are you sure you want to delete the channel?
-
+
@@ -1149,12 +1149,12 @@ Are you sure you want to delete the channel?
-
+
-
+
@@ -1164,7 +1164,7 @@ Are you sure you want to delete the channel?
-
+
@@ -1189,7 +1189,7 @@ Are you sure you want to delete the channel?
-
+
@@ -1209,7 +1209,7 @@ Are you sure you want to delete the channel?
-
+
@@ -1221,17 +1221,17 @@ There is no required difficulty for version 2 addresses like this.
+Receiver's required difficulty: {0} and {1}
-
+
-
+
@@ -1241,7 +1241,7 @@ Receiver's required difficulty: %1 and %2
-
+
@@ -1256,12 +1256,12 @@ Receiver's required difficulty: %1 and %2
-
+
-
+
@@ -1623,27 +1623,27 @@ The 'Random Number' option is selected by default but deterministic ad
-
+
-
+
-
+
-
+
-
+
diff --git a/src/translations/bitmessage_no.qm b/src/translations/bitmessage_no.qm
index 493d09ef..9f2d9efb 100644
Binary files a/src/translations/bitmessage_no.qm and b/src/translations/bitmessage_no.qm differ
diff --git a/src/translations/bitmessage_no.ts b/src/translations/bitmessage_no.ts
index bb1d5278..d0ab3946 100644
--- a/src/translations/bitmessage_no.ts
+++ b/src/translations/bitmessage_no.ts
@@ -241,8 +241,8 @@ Please type the desired email address (including @mailchuck.com) below:
-
- En av dine gamle adresser er av den første typen og derfor ikke lenger støttet: %1. Derfor kan den vel slettes?
+
+ En av dine gamle adresser er av den første typen og derfor ikke lenger støttet: {0}. Derfor kan den vel slettes?
@@ -261,13 +261,13 @@ Please type the desired email address (including @mailchuck.com) below:
-
- Beskjed sendt. Venter på bekreftelse. Sendt %1
+
+ Beskjed sendt. Venter på bekreftelse. Sendt {0}
-
- Beskjed sendt. Sendt %1
+
+ Beskjed sendt. Sendt {0}
@@ -276,8 +276,8 @@ Please type the desired email address (including @mailchuck.com) below:
-
- Bekreftelse på beskjeden mottatt %1
+
+ Bekreftelse på beskjeden mottatt {0}
@@ -286,18 +286,18 @@ Please type the desired email address (including @mailchuck.com) below:
-
- Kringkasting på %1
+
+ Kringkasting på {0}
-
- Problem: Det nødvendige arbeidet som kreves utført av mottaker er mer krevende enn det som er satt som akseptabelt. %1
+
+ Problem: Det nødvendige arbeidet som kreves utført av mottaker er mer krevende enn det som er satt som akseptabelt. {0}
-
- Problem: Mottakerens nøkkel kunne ikke brukes til å kryptere beskjeden. %1
+
+ Problem: Mottakerens nøkkel kunne ikke brukes til å kryptere beskjeden. {0}
@@ -306,8 +306,8 @@ Please type the desired email address (including @mailchuck.com) below:
-
- Ukjent status: %1 %2
+
+ Ukjent status: {0} {1}
@@ -347,10 +347,10 @@ Please type the desired email address (including @mailchuck.com) below:
Du kan administrere nøklene dine ved å endre filen keys.dat lagret i
- %1
+ {0}
Det er viktig at du tar en sikkerhetskopi av denne filen.
@@ -366,10 +366,10 @@ Det er viktig at du tar en sikkerhetskopi av denne filen.
Du kan administrere dine nøkler ved å endre på filen keys.dat lagret i
- %1
+ {0}
Det er viktig at du tar sikkerhetskopi av denne filen. Vil du åpne denne filen nå? (Vær sikker på å få avsluttet Bitmessage før du gjør endringer.)
@@ -434,8 +434,8 @@ Det er viktig at du tar sikkerhetskopi av denne filen. Vil du åpne denne filen
-
- Opprettet ny kanal. For å la andre delta i din nye kanal gir du dem dem kanalnavnet og denne Bitmessage-adressen: %1. Denne adressen vises også i 'Dine identiteter'.
+
+ Opprettet ny kanal. For å la andre delta i din nye kanal gir du dem dem kanalnavnet og denne Bitmessage-adressen: {0}. Denne adressen vises også i 'Dine identiteter'.
@@ -502,53 +502,53 @@ Det er viktig at du tar sikkerhetskopi av denne filen. Vil du åpne denne filen
-
+
-
+
-
- Feil: Bitmessage-adresser begynner med BM-. Vennligst sjekk %1
+
+ Feil: Bitmessage-adresser begynner med BM-. Vennligst sjekk {0}
-
- Feil: Adressen %1 er skrevet eller kopiert inn feil. Vennligst sjekk den.
+
+ Feil: Adressen {0} er skrevet eller kopiert inn feil. Vennligst sjekk den.
-
- Feil: Adressen %1 innerholder ugyldige tegn. Vennligst sjekk den.
+
+ Feil: Adressen {0} innerholder ugyldige tegn. Vennligst sjekk den.
-
- Feil: Typenummeret for adressen %1 er for høy. Enten trenger du å oppgradere Bitmessaage-programvaren eller så er det fordi kontakten din har funnet på noe smart.
+
+ Feil: Typenummeret for adressen {0} er for høy. Enten trenger du å oppgradere Bitmessaage-programvaren eller så er det fordi kontakten din har funnet på noe smart.
-
- Feil: Noen av de kodede dataene i adressen %1 er for korte. Det kan hende det er noe galt med programvaren til kontakten din.
+
+ Feil: Noen av de kodede dataene i adressen {0} er for korte. Det kan hende det er noe galt med programvaren til kontakten din.
-
- Feil: Noen av de kodede dataene i adressen %1 er for lange. Det kan hende det er noe galt med programvaren til kontakten din.
+
+ Feil: Noen av de kodede dataene i adressen {0} er for lange. Det kan hende det er noe galt med programvaren til kontakten din.
-
+
-
- Feil: Noe er galt med adressen %1.
+
+ Feil: Noe er galt med adressen {0}.
@@ -562,8 +562,8 @@ Det er viktig at du tar sikkerhetskopi av denne filen. Vil du åpne denne filen
-
- Angående adressen %1, Bitmessage forstår ikke adressetypenumre for %2. Oppdater Bitmessage til siste versjon.
+
+ Angående adressen {0}, Bitmessage forstår ikke adressetypenumre for {1}. Oppdater Bitmessage til siste versjon.
@@ -572,8 +572,8 @@ Det er viktig at du tar sikkerhetskopi av denne filen. Vil du åpne denne filen
-
- Angående adressen %1, Bitmessage kan ikke håndtere strømnumre for %2. Oppdater Bitmessage til siste utgivelse.
+
+ Angående adressen {0}, Bitmessage kan ikke håndtere strømnumre for {1}. Oppdater Bitmessage til siste utgivelse.
@@ -707,8 +707,8 @@ Det er viktig at du tar sikkerhetskopi av denne filen. Vil du åpne denne filen
-
- Bitmessage kan ikke finne adressen %1. Kanskje du fjernet den?
+
+ Bitmessage kan ikke finne adressen {0}. Kanskje du fjernet den?
@@ -861,8 +861,8 @@ Are you sure you want to delete the channel?
-
- Du benytter TCP-port %1. (Dette kan endres på i innstillingene).
+
+ Du benytter TCP-port {0}. (Dette kan endres på i innstillingene).
@@ -1056,8 +1056,8 @@ Are you sure you want to delete the channel?
-
- Zoom nivå %1%
+
+ Zoom nivå {0}%
@@ -1071,47 +1071,47 @@ Are you sure you want to delete the channel?
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
@@ -1132,7 +1132,7 @@ Are you sure you want to delete the channel?
-
+
@@ -1152,12 +1152,12 @@ Are you sure you want to delete the channel?
-
+
-
+
@@ -1167,7 +1167,7 @@ Are you sure you want to delete the channel?
-
+
@@ -1192,7 +1192,7 @@ Are you sure you want to delete the channel?
-
+
@@ -1212,7 +1212,7 @@ Are you sure you want to delete the channel?
-
+
@@ -1224,17 +1224,17 @@ There is no required difficulty for version 2 addresses like this.
+Receiver's required difficulty: {0} and {1}
-
+
-
+
@@ -1244,7 +1244,7 @@ Receiver's required difficulty: %1 and %2
-
+
@@ -1259,12 +1259,12 @@ Receiver's required difficulty: %1 and %2
-
+
-
+
@@ -1627,27 +1627,27 @@ The 'Random Number' option is selected by default but deterministic ad
-
- Siden oppstart på %1
+
+ Siden oppstart på {0}
-
- Ned: %1/s Totalt: %2
+
+ Ned: {0}/s Totalt: {1}
-
- Opp: %1/s Totalt: %2
+
+ Opp: {0}/s Totalt: {1}
-
- Antall tilkoblinger: %1
+
+ Antall tilkoblinger: {0}
-
+
diff --git a/src/translations/bitmessage_pl.qm b/src/translations/bitmessage_pl.qm
index fb31e8d5..215ad8cf 100644
Binary files a/src/translations/bitmessage_pl.qm and b/src/translations/bitmessage_pl.qm differ
diff --git a/src/translations/bitmessage_pl.ts b/src/translations/bitmessage_pl.ts
index 89c6162e..ee8a3e89 100644
--- a/src/translations/bitmessage_pl.ts
+++ b/src/translations/bitmessage_pl.ts
@@ -354,8 +354,8 @@ Please type the desired email address (including @mailchuck.com) below:
-
- Jeden z adresów, %1, jest starym adresem wersji 1. Adresy tej wersji nie są już wspierane. Usunąć go?
+
+ Jeden z adresów, {0}, jest starym adresem wersji 1. Adresy tej wersji nie są już wspierane. Usunąć go?
@@ -374,13 +374,13 @@ Please type the desired email address (including @mailchuck.com) below:
-
- Wiadomość wysłana. Oczekiwanie na potwierdzenie odbioru. Wysłano o %1
+
+ Wiadomość wysłana. Oczekiwanie na potwierdzenie odbioru. Wysłano o {0}
-
- Wiadomość wysłana. Wysłano o %1
+
+ Wiadomość wysłana. Wysłano o {0}
@@ -389,8 +389,8 @@ Please type the desired email address (including @mailchuck.com) below:
-
- Otrzymano potwierdzenie odbioru wiadomości %1
+
+ Otrzymano potwierdzenie odbioru wiadomości {0}
@@ -399,18 +399,18 @@ Please type the desired email address (including @mailchuck.com) below:
-
- Wysłana o %1
+
+ Wysłana o {0}
-
- Problem: dowód pracy wymagany przez odbiorcę jest trudniejszy niż zaakceptowany przez Ciebie. %1
+
+ Problem: dowód pracy wymagany przez odbiorcę jest trudniejszy niż zaakceptowany przez Ciebie. {0}
-
- Problem: klucz szyfrujący odbiorcy jest nieprawidłowy. Nie można zaszyfrować wiadomości. %1
+
+ Problem: klucz szyfrujący odbiorcy jest nieprawidłowy. Nie można zaszyfrować wiadomości. {0}
@@ -419,8 +419,8 @@ Please type the desired email address (including @mailchuck.com) below:
-
- Nieznany status: %1 %2
+
+ Nieznany status: {0} {1}
@@ -460,10 +460,10 @@ Please type the desired email address (including @mailchuck.com) below:
Możesz zarządzać swoimi kluczami edytując plik keys.dat znajdujący się
-%1
+{0}
Zaleca się zrobienie kopii zapasowej tego pliku.
@@ -479,10 +479,10 @@ Zaleca się zrobienie kopii zapasowej tego pliku.
Możesz zarządzać swoimi kluczami edytując plik keys.dat znajdujący się
-%1
+{0}
Zaleca się zrobienie kopii zapasowej tego pliku. Czy chcesz otworzyć ten plik teraz? (Zamknij Bitmessage przed wprowadzeniem jakichkolwiek zmian.)
@@ -547,7 +547,7 @@ Zaleca się zrobienie kopii zapasowej tego pliku. Czy chcesz otworzyć ten plik
-
+
@@ -618,52 +618,52 @@ Zwykle 4-5 dniowy TTL jest odpowiedni.
-
- Wiadomość jest za długa o %1 bajtów (maksymalna długość wynosi 261644 bajty). Przed wysłaniem należy ją skrócić.
+
+ Wiadomość jest za długa o {0} bajtów (maksymalna długość wynosi 261644 bajty). Przed wysłaniem należy ją skrócić.
-
- Błąd: Twoje konto nie było zarejestrowane w bramce poczty. Rejestrowanie jako %1, proszę poczekać na zakończenie procesu przed ponowną próbą wysłania wiadomości.
+
+ Błąd: Twoje konto nie było zarejestrowane w bramce poczty. Rejestrowanie jako {0}, proszę poczekać na zakończenie procesu przed ponowną próbą wysłania wiadomości.
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
@@ -678,8 +678,8 @@ Zwykle 4-5 dniowy TTL jest odpowiedni.
-
- Odnośnie adresu %1, Bitmessage nie potrafi odczytać wersji adresu %2. Może uaktualnij Bitmessage do najnowszej wersji.
+
+ Odnośnie adresu {0}, Bitmessage nie potrafi odczytać wersji adresu {1}. Może uaktualnij Bitmessage do najnowszej wersji.
@@ -688,8 +688,8 @@ Zwykle 4-5 dniowy TTL jest odpowiedni.
-
- Odnośnie adresu %1, Bitmessage nie potrafi operować na strumieniu adresu %2. Może uaktualnij Bitmessage do najnowszej wersji.
+
+ Odnośnie adresu {0}, Bitmessage nie potrafi operować na strumieniu adresu {1}. Może uaktualnij Bitmessage do najnowszej wersji.
@@ -823,8 +823,8 @@ Zwykle 4-5 dniowy TTL jest odpowiedni.
-
- Bitmessage nie może odnaleźć Twojego adresu %1. Może go usunąłeś?
+
+ Bitmessage nie może odnaleźć Twojego adresu {0}. Może go usunąłeś?
@@ -981,7 +981,7 @@ Czy na pewno chcesz usunąć ten kanał?
-
+
@@ -1176,8 +1176,8 @@ Czy na pewno chcesz usunąć ten kanał?
-
- Poziom powiększenia %1%
+
+ Poziom powiększenia {0}%
@@ -1191,48 +1191,48 @@ Czy na pewno chcesz usunąć ten kanał?
-
+
-
- Nowa wersja Bitmessage jest dostępna: %1. Pobierz ją z https://github.com/Bitmessage/PyBitmessage/releases/latest
+
+ Nowa wersja Bitmessage jest dostępna: {0}. Pobierz ją z https://github.com/Bitmessage/PyBitmessage/releases/latest
-
- Oczekiwanie na wykonanie dowodu pracy… %1%
+
+ Oczekiwanie na wykonanie dowodu pracy… {0}%
-
- Zamykanie PyBitmessage… %1%
+
+ Zamykanie PyBitmessage… {0}%
-
- Oczekiwanie na wysłanie obiektów… %1%
+
+ Oczekiwanie na wysłanie obiektów… {0}%
-
- Zapisywanie ustawień… %1%
+
+ Zapisywanie ustawień… {0}%
-
- Zamykanie rdzenia programu… %1%
+
+ Zamykanie rdzenia programu… {0}%
-
- Zatrzymywanie powiadomień… %1%
+
+ Zatrzymywanie powiadomień… {0}%
-
- Zaraz zamknę… %1%
+
+ Zaraz zamknę… {0}%
@@ -1246,8 +1246,8 @@ Czy na pewno chcesz usunąć ten kanał?
-
- Zamykanie PyBitmessage… %1%
+
+ Zamykanie PyBitmessage… {0}%
@@ -1266,13 +1266,13 @@ Czy na pewno chcesz usunąć ten kanał?
-
- Generowanie %1 nowych adresów.
+
+ Generowanie {0} nowych adresów.
-
- %1 jest już w 'Twoich tożsamościach'. Nie zostanie tu dodany.
+
+ {0} jest już w 'Twoich tożsamościach'. Nie zostanie tu dodany.
@@ -1281,7 +1281,7 @@ Czy na pewno chcesz usunąć ten kanał?
-
+
@@ -1306,8 +1306,8 @@ Czy na pewno chcesz usunąć ten kanał?
-
- Wysłano: %1
+
+ Wysłano: {0}
@@ -1326,8 +1326,8 @@ Czy na pewno chcesz usunąć ten kanał?
-
- Problem: adres docelowy jest urządzeniem przenośnym, które wymaga, aby adres docelowy był zawarty w wiadomości, ale jest to zabronione w Twoich ustawieniach. %1
+
+ Problem: adres docelowy jest urządzeniem przenośnym, które wymaga, aby adres docelowy był zawarty w wiadomości, ale jest to zabronione w Twoich ustawieniach. {0}
@@ -1339,19 +1339,19 @@ Nie ma wymaganej trudności dla adresów w wersji 2, takich jak ten adres.
+Receiver's required difficulty: {0} and {1}
Wykonywanie dowodu pracy niezbędnego do wysłania wiadomości.
-Odbiorca wymaga trudności: %1 i %2
+Odbiorca wymaga trudności: {0} i {1}
-
- Problem: dowód pracy wymagany przez odbiorcę (%1 i %2) jest trudniejszy niż chciałbyś wykonać. %3
+
+ Problem: dowód pracy wymagany przez odbiorcę ({0} i {1}) jest trudniejszy niż chciałbyś wykonać. {2}
-
- Problem: próbujesz wysłać wiadomość do siebie lub na kanał, ale Twój klucz szyfrujący nie został znaleziony w pliku keys.dat. Nie można zaszyfrować wiadomości. %1
+
+ Problem: próbujesz wysłać wiadomość do siebie lub na kanał, ale Twój klucz szyfrujący nie został znaleziony w pliku keys.dat. Nie można zaszyfrować wiadomości. {0}
@@ -1360,8 +1360,8 @@ Odbiorca wymaga trudności: %1 i %2
-
- Wiadomość wysłana. Oczekiwanie na potwierdzenie odbioru. Wysłano o %1
+
+ Wiadomość wysłana. Oczekiwanie na potwierdzenie odbioru. Wysłano o {0}
@@ -1375,13 +1375,13 @@ Odbiorca wymaga trudności: %1 i %2
-
- Wysyłanie prośby o klucz publiczny. Oczekiwanie na odpowiedź. Zapytano o %1
+
+ Wysyłanie prośby o klucz publiczny. Oczekiwanie na odpowiedź. Zapytano o {0}
-
- Mapowanie portów UPnP wykonano na porcie %1
+
+ Mapowanie portów UPnP wykonano na porcie {0}
@@ -1425,18 +1425,18 @@ Odbiorca wymaga trudności: %1 i %2
-
- Ksywka %1 nie została znaleziona.
+
+ Ksywka {0} nie została znaleziona.
-
- Zapytanie namecoin nie powiodło się (%1)
+
+ Zapytanie namecoin nie powiodło się ({0})
-
- Nieznany typ interfejsu namecoin: %1
+
+ Nieznany typ interfejsu namecoin: {0}
@@ -1445,13 +1445,13 @@ Odbiorca wymaga trudności: %1 i %2
-
- Ksywka %1 nie ma powiązanego adresu Bitmessage.
+
+ Ksywka {0} nie ma powiązanego adresu Bitmessage.
-
- Namecoind wersja %1 działa poprawnie!
+
+ Namecoind wersja {0} działa poprawnie!
@@ -1514,53 +1514,53 @@ Witamy w przyjaznym i bezpiecznym Bitmessage
-
- Błąd: adresy Bitmessage zaczynają się od BM-. Proszę sprawdzić adres odbiorcy %1.
+
+ Błąd: adresy Bitmessage zaczynają się od BM-. Proszę sprawdzić adres odbiorcy {0}.
-
- Błąd: adres odbiorcy %1 nie został skopiowany lub przepisany poprawnie. Proszę go sprawdzić.
+
+ Błąd: adres odbiorcy {0} nie został skopiowany lub przepisany poprawnie. Proszę go sprawdzić.
-
- Błąd: adres odbiorcy %1 zawiera nieprawidłowe znaki. Proszę go sprawdzić.
+
+ Błąd: adres odbiorcy {0} zawiera nieprawidłowe znaki. Proszę go sprawdzić.
-
- Błąd: wersja adresu odbiorcy %1 jest za wysoka. Musisz albo zaktualizować Twoje oprogramowanie Bitmessage, albo twój znajomy Cię trolluje.
+
+ Błąd: wersja adresu odbiorcy {0} jest za wysoka. Musisz albo zaktualizować Twoje oprogramowanie Bitmessage, albo twój znajomy Cię trolluje.
-
- Błąd: niektóre dane zakodowane w adresie odbiorcy %1 są zbyt krótkie. Być może coś nie działa należycie w programie Twojego znajomego.
+
+ Błąd: niektóre dane zakodowane w adresie odbiorcy {0} są zbyt krótkie. Być może coś nie działa należycie w programie Twojego znajomego.
-
- Błąd: niektóre dane zakodowane w adresie odbiorcy %1 są zbyt długie. Być może coś nie działa należycie w programie Twojego znajomego.
+
+ Błąd: niektóre dane zakodowane w adresie odbiorcy {0} są zbyt długie. Być może coś nie działa należycie w programie Twojego znajomego.
-
- Błąd: niektóre dane zakodowane w adresie odbiorcy %1 są uszkodzone. Być może coś nie działa należycie w programie Twojego znajomego.
+
+ Błąd: niektóre dane zakodowane w adresie odbiorcy {0} są uszkodzone. Być może coś nie działa należycie w programie Twojego znajomego.
-
- Błąd: coś jest nie tak z adresem odbiorcy %1.
+
+ Błąd: coś jest nie tak z adresem odbiorcy {0}.
-
- Błąd: %1
+
+ Błąd: {0}
-
- Od %1
+
+ Od {0}
@@ -1672,8 +1672,8 @@ Witamy w przyjaznym i bezpiecznym Bitmessage
-
- Odnośnik "%1" zostanie otwarty w przeglądarce. Może to spowodować zagrożenie bezpieczeństwa, może on ujawnić Twoją anonimowość lub pobrać złośliwe dane. Czy jesteś pewien?
+
+ Odnośnik "{0}" zostanie otwarty w przeglądarce. Może to spowodować zagrożenie bezpieczeństwa, może on ujawnić Twoją anonimowość lub pobrać złośliwe dane. Czy jesteś pewien?
@@ -2008,8 +2008,8 @@ Generowanie adresów „losowych” jest wybrane domyślnie, jednak deterministy
-
- Btimessage używa portu TCP %1. (Można go zmienić w ustawieniach).
+
+ Btimessage używa portu TCP {0}. (Można go zmienić w ustawieniach).
@@ -2061,28 +2061,28 @@ Generowanie adresów „losowych” jest wybrane domyślnie, jednak deterministy
-
- Od startu programu o %1
+
+ Od startu programu o {0}
-
- Pobieranie: %1/s W całości: %2
+
+ Pobieranie: {0}/s W całości: {1}
-
- Wysyłanie: %1/s W całości: %2
+
+ Wysyłanie: {0}/s W całości: {1}
-
- Wszystkich połączeń: %1
+
+ Wszystkich połączeń: {0}
-
- Zapytań o elementy na sekundę: %1
+
+ Zapytań o elementy na sekundę: {0}
@@ -2247,8 +2247,8 @@ Generowanie adresów „losowych” jest wybrane domyślnie, jednak deterministy
newchandialog
-
- Pomyślnie utworzono / dołączono do kanału %1
+
+ Pomyślnie utworzono / dołączono do kanału {0}
diff --git a/src/translations/bitmessage_pt.qm b/src/translations/bitmessage_pt.qm
index 9c6b3402..6a8aed93 100644
Binary files a/src/translations/bitmessage_pt.qm and b/src/translations/bitmessage_pt.qm differ
diff --git a/src/translations/bitmessage_pt.ts b/src/translations/bitmessage_pt.ts
index 8c43b926..7aeed231 100644
--- a/src/translations/bitmessage_pt.ts
+++ b/src/translations/bitmessage_pt.ts
@@ -241,7 +241,7 @@ Please type the desired email address (including @mailchuck.com) below:
-
+
@@ -261,12 +261,12 @@ Please type the desired email address (including @mailchuck.com) below:
-
- Mensagem enviada. Aguardando confirmação. Enviada a %1
+
+ Mensagem enviada. Aguardando confirmação. Enviada a {0}
-
+
Mensagem enviada. Enviada a 1%
@@ -276,7 +276,7 @@ Please type the desired email address (including @mailchuck.com) below:
-
+
@@ -286,17 +286,17 @@ Please type the desired email address (including @mailchuck.com) below:
-
+
-
+
-
+
@@ -306,7 +306,7 @@ Please type the desired email address (including @mailchuck.com) below:
-
+
@@ -347,7 +347,7 @@ Please type the desired email address (including @mailchuck.com) below:
@@ -364,7 +364,7 @@ It is important that you back up this file.
@@ -430,7 +430,7 @@ It is important that you back up this file. Would you like to open the file now?
-
+
@@ -498,52 +498,52 @@ It is important that you back up this file. Would you like to open the file now?
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
@@ -558,7 +558,7 @@ It is important that you back up this file. Would you like to open the file now?
-
+
@@ -568,7 +568,7 @@ It is important that you back up this file. Would you like to open the file now?
-
+
@@ -703,7 +703,7 @@ It is important that you back up this file. Would you like to open the file now?
-
+
@@ -857,7 +857,7 @@ Are you sure you want to delete the channel?
-
+
@@ -1052,7 +1052,7 @@ Are you sure you want to delete the channel?
-
+
@@ -1067,47 +1067,47 @@ Are you sure you want to delete the channel?
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
@@ -1128,7 +1128,7 @@ Are you sure you want to delete the channel?
-
+
@@ -1148,12 +1148,12 @@ Are you sure you want to delete the channel?
-
+
-
+
@@ -1163,7 +1163,7 @@ Are you sure you want to delete the channel?
-
+
@@ -1188,7 +1188,7 @@ Are you sure you want to delete the channel?
-
+
@@ -1208,7 +1208,7 @@ Are you sure you want to delete the channel?
-
+
@@ -1220,17 +1220,17 @@ There is no required difficulty for version 2 addresses like this.
+Receiver's required difficulty: {0} and {1}
-
+
-
+
@@ -1240,7 +1240,7 @@ Receiver's required difficulty: %1 and %2
-
+
@@ -1255,12 +1255,12 @@ Receiver's required difficulty: %1 and %2
-
+
-
+
@@ -1622,27 +1622,27 @@ The 'Random Number' option is selected by default but deterministic ad
-
+
-
+
-
+
-
+
-
+
diff --git a/src/translations/bitmessage_ru.qm b/src/translations/bitmessage_ru.qm
index 8c0269b9..2642035b 100644
Binary files a/src/translations/bitmessage_ru.qm and b/src/translations/bitmessage_ru.qm differ
diff --git a/src/translations/bitmessage_ru.ts b/src/translations/bitmessage_ru.ts
index 4a80f62e..0fac3766 100644
--- a/src/translations/bitmessage_ru.ts
+++ b/src/translations/bitmessage_ru.ts
@@ -314,8 +314,8 @@ Please type the desired email address (including @mailchuck.com) below:
-
- Один из Ваших адресов, %1, является устаревшим адресом версии 1. Адреса версии 1 больше не поддерживаются. Хотите ли Вы удалить его сейчас?
+
+ Один из Ваших адресов, {0}, является устаревшим адресом версии 1. Адреса версии 1 больше не поддерживаются. Хотите ли Вы удалить его сейчас?
@@ -334,13 +334,13 @@ Please type the desired email address (including @mailchuck.com) below:
-
- Сообщение отправлено. Ожидаем подтверждения. Отправлено в %1
+
+ Сообщение отправлено. Ожидаем подтверждения. Отправлено в {0}
-
- Сообщение отправлено в %1
+
+ Сообщение отправлено в {0}
@@ -349,8 +349,8 @@ Please type the desired email address (including @mailchuck.com) below:
-
- Доставлено в %1
+
+ Доставлено в {0}
@@ -359,18 +359,18 @@ Please type the desired email address (including @mailchuck.com) below:
-
- Рассылка на %1
+
+ Рассылка на {0}
-
- Проблема: Ваш получатель требует более сложных вычислений, чем максимум, указанный в Ваших настройках. %1
+
+ Проблема: Ваш получатель требует более сложных вычислений, чем максимум, указанный в Ваших настройках. {0}
-
- Проблема: ключ получателя неправильный. Невозможно зашифровать сообщение. %1
+
+ Проблема: ключ получателя неправильный. Невозможно зашифровать сообщение. {0}
@@ -379,8 +379,8 @@ Please type the desired email address (including @mailchuck.com) below:
-
- Неизвестный статус: %1 %2
+
+ Неизвестный статус: {0} {1}
@@ -421,10 +421,10 @@ Please type the desired email address (including @mailchuck.com) below:
Вы можете управлять Вашими ключами, редактируя файл keys.dat, находящийся в
- %1
+ {0}
Создайте резервную копию этого файла перед тем как будете его редактировать.
@@ -442,7 +442,7 @@ It is important that you back up this file.
@@ -508,7 +508,7 @@ It is important that you back up this file. Would you like to open the file now?
-
+
@@ -579,52 +579,52 @@ It is important that you back up this file. Would you like to open the file now?
-
- Сообщение, которое вы пытаетесь отправить, длиннее максимально допустимого на %1 байт. (Максимально допустимое значение 261644 байта). Пожалуйста, сократите сообщение перед отправкой.
+
+ Сообщение, которое вы пытаетесь отправить, длиннее максимально допустимого на {0} байт. (Максимально допустимое значение 261644 байта). Пожалуйста, сократите сообщение перед отправкой.
-
- Ошибка: ваш аккаунт не зарегистрирован на Email-шлюзе. Отправка регистрации %1, пожалуйста, подождите пока процесс регистрации не завершится, прежде чем попытаться отправить сообщение заново.
+
+ Ошибка: ваш аккаунт не зарегистрирован на Email-шлюзе. Отправка регистрации {0}, пожалуйста, подождите пока процесс регистрации не завершится, прежде чем попытаться отправить сообщение заново.
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
@@ -639,8 +639,8 @@ It is important that you back up this file. Would you like to open the file now?
-
- По поводу адреса %1: Bitmessage не поддерживает адреса версии %2. Возможно вам нужно обновить клиент Bitmessage.
+
+ По поводу адреса {0}: Bitmessage не поддерживает адреса версии {1}. Возможно вам нужно обновить клиент Bitmessage.
@@ -649,8 +649,8 @@ It is important that you back up this file. Would you like to open the file now?
-
- По поводу адреса %1: Bitmessage не поддерживает поток номер %2. Возможно вам нужно обновить клиент Bitmessage.
+
+ По поводу адреса {0}: Bitmessage не поддерживает поток номер {1}. Возможно вам нужно обновить клиент Bitmessage.
@@ -784,8 +784,8 @@ It is important that you back up this file. Would you like to open the file now?
-
- Bitmessage не может найти Ваш адрес %1. Возможно Вы удалили его?
+
+ Bitmessage не может найти Ваш адрес {0}. Возможно Вы удалили его?
@@ -942,7 +942,7 @@ Are you sure you want to delete the channel?
-
+
@@ -1137,8 +1137,8 @@ Are you sure you want to delete the channel?
-
- Увеличение %1%
+
+ Увеличение {0}%
@@ -1152,48 +1152,48 @@ Are you sure you want to delete the channel?
-
+
-
- Доступна новая версия PyBitmessage: %1. Загрузите её: https://github.com/Bitmessage/PyBitmessage/releases/latest
+
+ Доступна новая версия PyBitmessage: {0}. Загрузите её: https://github.com/Bitmessage/PyBitmessage/releases/latest
-
- Ожидание окончания PoW... %1%
+
+ Ожидание окончания PoW... {0}%
-
- Завершение PyBitmessage... %1%
+
+ Завершение PyBitmessage... {0}%
-
- Ожидание отправки объектов... %1%
+
+ Ожидание отправки объектов... {0}%
-
- Сохранение настроек... %1%
+
+ Сохранение настроек... {0}%
-
- Завершение работы ядра... %1%
+
+ Завершение работы ядра... {0}%
-
- Остановка сервиса уведомлений... %1%
+
+ Остановка сервиса уведомлений... {0}%
-
- Завершение вот-вот произойдет... %1%
+
+ Завершение вот-вот произойдет... {0}%
@@ -1207,8 +1207,8 @@ Are you sure you want to delete the channel?
-
- Завершение PyBitmessage... %1%
+
+ Завершение PyBitmessage... {0}%
@@ -1227,13 +1227,13 @@ Are you sure you want to delete the channel?
-
- Создание %1 новых адресов.
+
+ Создание {0} новых адресов.
-
- %1 уже имеется в ваших адресах. Не добавляю его снова.
+
+ {0} уже имеется в ваших адресах. Не добавляю его снова.
@@ -1242,7 +1242,7 @@ Are you sure you want to delete the channel?
-
+
@@ -1267,8 +1267,8 @@ Are you sure you want to delete the channel?
-
- Рассылка отправлена на %1
+
+ Рассылка отправлена на {0}
@@ -1287,8 +1287,8 @@ Are you sure you want to delete the channel?
-
- Проблема: адресат является мобильным устройством, которое требует, чтобы адрес назначения был включен в сообщение, однако, это запрещено в ваших настройках. %1
+
+ Проблема: адресат является мобильным устройством, которое требует, чтобы адрес назначения был включен в сообщение, однако, это запрещено в ваших настройках. {0}
@@ -1300,19 +1300,19 @@ There is no required difficulty for version 2 addresses like this.
+Receiver's required difficulty: {0} and {1}
Выполнение работы, требуемой для отправки сообщения.
-Получатель запросил сложность: %1 и %2
+Получатель запросил сложность: {0} и {1}
-
- Проблема: сложность, затребованная получателем (%1 и %2) гораздо больше, чем вы готовы сделать. %3
+
+ Проблема: сложность, затребованная получателем ({0} и {1}) гораздо больше, чем вы готовы сделать. {2}
-
- Проблема: вы пытаетесь отправить сообщение самому себе или в чан, но ваш ключ шифрования не найден в файле ключей keys.dat. Невозможно зашифровать сообщение. %1
+
+ Проблема: вы пытаетесь отправить сообщение самому себе или в чан, но ваш ключ шифрования не найден в файле ключей keys.dat. Невозможно зашифровать сообщение. {0}
@@ -1321,8 +1321,8 @@ Receiver's required difficulty: %1 and %2
-
- Отправлено. Ожидаем подтверждения. Отправлено в %1
+
+ Отправлено. Ожидаем подтверждения. Отправлено в {0}
@@ -1336,13 +1336,13 @@ Receiver's required difficulty: %1 and %2
-
- Отправка запроса открытого ключа шифрования. Ожидание ответа. Запрошено в %1
+
+ Отправка запроса открытого ключа шифрования. Ожидание ответа. Запрошено в {0}
-
- Распределение портов UPnP завершилось выделением порта %1
+
+ Распределение портов UPnP завершилось выделением порта {0}
@@ -1386,13 +1386,13 @@ Receiver's required difficulty: %1 and %2
-
- Имя %1 не найдено.
+
+ Имя {0} не найдено.
-
- Запрос к namecoin не удался (%1).
+
+ Запрос к namecoin не удался ({0}).
@@ -1401,18 +1401,18 @@ Receiver's required difficulty: %1 and %2
-
- Имя %1 не содержит корректных данных JSON.
+
+ Имя {0} не содержит корректных данных JSON.
-
- Имя %1 не имеет связанного адреса Bitmessage.
+
+ Имя {0} не имеет связанного адреса Bitmessage.
-
- Успех! Namecoind версии %1 работает.
+
+ Успех! Namecoind версии {0} работает.
@@ -1475,53 +1475,53 @@ Receiver's required difficulty: %1 and %2
-
- Ошибка: адреса Bitmessage начинаются с "BM-". Пожалуйста, проверьте адрес получателя %1.
+
+ Ошибка: адреса Bitmessage начинаются с "BM-". Пожалуйста, проверьте адрес получателя {0}.
-
- Ошибка: адрес получателя %1 набран или скопирован неправильно. Пожалуйста, проверьте его.
+
+ Ошибка: адрес получателя {0} набран или скопирован неправильно. Пожалуйста, проверьте его.
-
- Ошибка: адрес получателя %1 содержит недопустимые символы. Пожалуйста, проверьте его.
+
+ Ошибка: адрес получателя {0} содержит недопустимые символы. Пожалуйста, проверьте его.
-
- Ошибка: версия адреса получателя %1 слишком высокая. Либо вам нужно обновить программу Bitmessage, либо ваш знакомый - умник.
+
+ Ошибка: версия адреса получателя {0} слишком высокая. Либо вам нужно обновить программу Bitmessage, либо ваш знакомый - умник.
-
- Ошибка: часть данных, закодированных в адресе получателя %1 слишком короткая. Видимо, что-то не так с программой, используемой вашим знакомым.
+
+ Ошибка: часть данных, закодированных в адресе получателя {0} слишком короткая. Видимо, что-то не так с программой, используемой вашим знакомым.
-
- Ошибка: часть данных, закодированных в адресе получателя %1 слишком длинная. Видимо, что-то не так с программой, используемой вашим знакомым.
+
+ Ошибка: часть данных, закодированных в адресе получателя {0} слишком длинная. Видимо, что-то не так с программой, используемой вашим знакомым.
-
- Ошибка: часть данных, закодированных в адресе получателя %1 сформирована неправильно. Видимо, что-то не так с программой, используемой вашим знакомым.
+
+ Ошибка: часть данных, закодированных в адресе получателя {0} сформирована неправильно. Видимо, что-то не так с программой, используемой вашим знакомым.
-
- Ошибка: что-то не так с адресом получателя %1.
+
+ Ошибка: что-то не так с адресом получателя {0}.
-
- Ошибка: %1
+
+ Ошибка: {0}
-
- От %1
+
+ От {0}
@@ -1566,7 +1566,7 @@ Receiver's required difficulty: %1 and %2
- Показать %1 прошлую рассылку с этого адреса.Показать %1 прошлых рассылки с этого адреса.Показать %1 прошлых рассылок с этого адреса.Показать %1 прошлых рассылок с этого адреса.
+ Показать {0} прошлую рассылку с этого адреса.Показать {0} прошлых рассылки с этого адреса.Показать {0} прошлых рассылок с этого адреса.Показать {0} прошлых рассылок с этого адреса.
@@ -1613,8 +1613,8 @@ Receiver's required difficulty: %1 and %2
-
- Ссылка "%1" откроется в браузере. Это может быть угрозой безопасности, например деанонимизировать вас или привести к скачиванию вредоносных данных. Вы уверены?
+
+ Ссылка "{0}" откроется в браузере. Это может быть угрозой безопасности, например деанонимизировать вас или привести к скачиванию вредоносных данных. Вы уверены?
@@ -1948,8 +1948,8 @@ The 'Random Number' option is selected by default but deterministic ad
-
- Вы используете TCP порт %1. (Его можно поменять в настройках).
+
+ Вы используете TCP порт {0}. (Его можно поменять в настройках).
@@ -2001,28 +2001,28 @@ The 'Random Number' option is selected by default but deterministic ad
-
- С начала работы, %1
+
+ С начала работы, {0}
-
- Загрузка: %1/s Всего: %2
+
+ Загрузка: {0}/s Всего: {1}
-
- Отправка: %1/s Всего: %2
+
+ Отправка: {0}/s Всего: {1}
-
- Всего соединений: %1
+
+ Всего соединений: {0}
-
- Поисков в каталоге в секунду: %1
+
+ Поисков в каталоге в секунду: {0}
@@ -2187,8 +2187,8 @@ The 'Random Number' option is selected by default but deterministic ad
newchandialog
-
- Успешно создан / подключен чан %1
+
+ Успешно создан / подключен чан {0}
diff --git a/src/translations/bitmessage_sk.qm b/src/translations/bitmessage_sk.qm
index 26c2a24d..b963125f 100644
Binary files a/src/translations/bitmessage_sk.qm and b/src/translations/bitmessage_sk.qm differ
diff --git a/src/translations/bitmessage_sk.ts b/src/translations/bitmessage_sk.ts
index 8c3b0209..c80d23dd 100644
--- a/src/translations/bitmessage_sk.ts
+++ b/src/translations/bitmessage_sk.ts
@@ -311,8 +311,8 @@ Please type the desired email address (including @mailchuck.com) below:
-
- Jedna z vašich adries, %1, je stará verzia adresy, 1. Verzie adresy 1 už nie sú podporované. Odstrániť ju teraz?
+
+ Jedna z vašich adries, {0}, je stará verzia adresy, 1. Verzie adresy 1 už nie sú podporované. Odstrániť ju teraz?
@@ -331,13 +331,13 @@ Please type the desired email address (including @mailchuck.com) below:
-
- Správa odoslaná. Čakanie na potvrdenie. Odoslaná %1
+
+ Správa odoslaná. Čakanie na potvrdenie. Odoslaná {0}
-
- Správa odoslaná. Odoslaná %1
+
+ Správa odoslaná. Odoslaná {0}
@@ -346,8 +346,8 @@ Please type the desired email address (including @mailchuck.com) below:
-
- Potvrdenie prijatia správy %1
+
+ Potvrdenie prijatia správy {0}
@@ -356,18 +356,18 @@ Please type the desired email address (including @mailchuck.com) below:
-
+
Rozoslané 1%
-
- Problém: práca požadovná príjemcom je oveľa ťažšia, než je povolené v nastaveniach. %1
+
+ Problém: práca požadovná príjemcom je oveľa ťažšia, než je povolené v nastaveniach. {0}
-
- Problém: šifrovací kľúč príjemcu je nesprávny. Nie je možné zašifrovať správu. %1
+
+ Problém: šifrovací kľúč príjemcu je nesprávny. Nie je možné zašifrovať správu. {0}
@@ -376,8 +376,8 @@ Please type the desired email address (including @mailchuck.com) below:
-
- Neznámy stav: %1 %2
+
+ Neznámy stav: {0} {1}
@@ -417,10 +417,10 @@ Please type the desired email address (including @mailchuck.com) below:
Kľúče môžete spravovať úpravou súboru keys.dat, ktorý je uložený v adresári
-%1
+{0}
Tento súbor je dôležité zálohovať.
@@ -436,10 +436,10 @@ Tento súbor je dôležité zálohovať.
Kľúče môžete spravovať úpravou súboru keys.dat, ktorý je uložený v adresári
-%1
+{0}
Tento súbor je dôležité zálohovať. Chcete tento súbor teraz otvoriť? (Nezabudnite zatvoriť Bitmessage pred vykonaním akýchkoľvek zmien.)
@@ -504,7 +504,7 @@ Tento súbor je dôležité zálohovať. Chcete tento súbor teraz otvoriť? (Ne
-
+
@@ -572,52 +572,52 @@ Tento súbor je dôležité zálohovať. Chcete tento súbor teraz otvoriť? (Ne
-
- Správa, ktorú skúšate poslať, má %1 bajtov naviac. (Maximum je 261 644 bajtov). Prosím pred odoslaním skrátiť.
+
+ Správa, ktorú skúšate poslať, má {0} bajtov naviac. (Maximum je 261 644 bajtov). Prosím pred odoslaním skrátiť.
-
- Chyba: Váš účet nebol registrovaný na e-mailovej bráne. Skúšam registrovať ako %1, prosím počkajte na spracovanie registrácie pred opakovaným odoslaním správy.
+
+ Chyba: Váš účet nebol registrovaný na e-mailovej bráne. Skúšam registrovať ako {0}, prosím počkajte na spracovanie registrácie pred opakovaným odoslaním správy.
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
@@ -632,8 +632,8 @@ Tento súbor je dôležité zálohovať. Chcete tento súbor teraz otvoriť? (Ne
-
- Čo sa týka adresy %1, Bitmessage nepozná číslo verzie adresy %2. Možno by ste mali upgradenúť Bitmessage na najnovšiu verziu.
+
+ Čo sa týka adresy {0}, Bitmessage nepozná číslo verzie adresy {1}. Možno by ste mali upgradenúť Bitmessage na najnovšiu verziu.
@@ -642,8 +642,8 @@ Tento súbor je dôležité zálohovať. Chcete tento súbor teraz otvoriť? (Ne
-
- Čo sa týka adresy %1, Bitmessage nespracováva číslo prúdu %2. Možno by ste mali upgradenúť Bitmessage na najnovšiu verziu.
+
+ Čo sa týka adresy {0}, Bitmessage nespracováva číslo prúdu {1}. Možno by ste mali upgradenúť Bitmessage na najnovšiu verziu.
@@ -777,8 +777,8 @@ Tento súbor je dôležité zálohovať. Chcete tento súbor teraz otvoriť? (Ne
-
- Bitmessage nemôže nájsť vašu adresu %1. Možno ste ju odstránili?
+
+ Bitmessage nemôže nájsť vašu adresu {0}. Možno ste ju odstránili?
@@ -935,7 +935,7 @@ Ste si istý, že chcete kanál odstrániť?
-
+
@@ -1130,8 +1130,8 @@ Ste si istý, že chcete kanál odstrániť?
-
- Úroveň priblíženia %1%
+
+ Úroveň priblíženia {0}%
@@ -1145,48 +1145,48 @@ Ste si istý, že chcete kanál odstrániť?
-
+
-
- K dispozícii je nová verzia PyBitmessage: %1. Môžete ju stiahnuť na https://github.com/Bitmessage/PyBitmessage/releases/latest
+
+ K dispozícii je nová verzia PyBitmessage: {0}. Môžete ju stiahnuť na https://github.com/Bitmessage/PyBitmessage/releases/latest
-
- Čakám na ukončenie práce... %1%
+
+ Čakám na ukončenie práce... {0}%
-
- Ukončujem PyBitmessage... %1%
+
+ Ukončujem PyBitmessage... {0}%
-
- Čakám na odoslanie objektov... %1%
+
+ Čakám na odoslanie objektov... {0}%
-
- Ukladám nastavenia... %1%
+
+ Ukladám nastavenia... {0}%
-
- Ukončujem jadro... %1%
+
+ Ukončujem jadro... {0}%
-
- Zastavujem oznámenia... %1%
+
+ Zastavujem oznámenia... {0}%
-
- Posledná fáza ukončenia... %1%
+
+ Posledná fáza ukončenia... {0}%
@@ -1200,8 +1200,8 @@ Ste si istý, že chcete kanál odstrániť?
-
- Ukončujem PyBitmessage... %1%
+
+ Ukončujem PyBitmessage... {0}%
@@ -1220,13 +1220,13 @@ Ste si istý, že chcete kanál odstrániť?
-
- Vytváram %1 nových adries.
+
+ Vytváram {0} nových adries.
-
- %1 sa už nachádza medzi vášmi identitami, nepridávam dvojmo.
+
+ {0} sa už nachádza medzi vášmi identitami, nepridávam dvojmo.
@@ -1235,7 +1235,7 @@ Ste si istý, že chcete kanál odstrániť?
-
+
@@ -1260,8 +1260,8 @@ Ste si istý, že chcete kanál odstrániť?
-
- Rozoslané %1
+
+ Rozoslané {0}
@@ -1280,8 +1280,8 @@ Ste si istý, že chcete kanál odstrániť?
-
- Problém: adresa príjemcu je na mobilnom zariadení a požaduje, aby správy obsahovali nezašifrovanú adresu príjemcu. Vaše nastavenia však túto možnost nemajú povolenú. %1
+
+ Problém: adresa príjemcu je na mobilnom zariadení a požaduje, aby správy obsahovali nezašifrovanú adresu príjemcu. Vaše nastavenia však túto možnost nemajú povolenú. {0}
@@ -1293,19 +1293,19 @@ Adresy verzie dva, ako táto, nepožadujú obtiažnosť.
+Receiver's required difficulty: {0} and {1}
Vykonávam prácu potrebnú na odoslanie správy.
-Priímcova požadovaná obtiažnosť: %1 a %2
+Priímcova požadovaná obtiažnosť: {0} a {1}
-
- Problém: Práca požadovná príjemcom (%1 a %2) je obtiažnejšia, ako máte povolené. %3
+
+ Problém: Práca požadovná príjemcom ({0} a {1}) je obtiažnejšia, ako máte povolené. {2}
-
- Problém: skúšate odslať správu sami sebe, ale nemôžem nájsť šifrovací kľúč v súbore keys.dat. Nemožno správu zašifrovať: %1
+
+ Problém: skúšate odslať správu sami sebe, ale nemôžem nájsť šifrovací kľúč v súbore keys.dat. Nemožno správu zašifrovať: {0}
@@ -1314,8 +1314,8 @@ Priímcova požadovaná obtiažnosť: %1 a %2
-
- Správa odoslaná. Čakanie na potvrdenie. Odoslaná %1
+
+ Správa odoslaná. Čakanie na potvrdenie. Odoslaná {0}
@@ -1329,13 +1329,13 @@ Priímcova požadovaná obtiažnosť: %1 a %2
-
- Odosielam požiadavku na verejný kľúč. Čakám na odpoveď. Vyžiadaný %1
+
+ Odosielam požiadavku na verejný kľúč. Čakám na odpoveď. Vyžiadaný {0}
-
- Mapovanie portov UPnP vytvorené na porte %1
+
+ Mapovanie portov UPnP vytvorené na porte {0}
@@ -1379,28 +1379,28 @@ Priímcova požadovaná obtiažnosť: %1 a %2
-
- Problém komunikácie s proxy: %1. Prosím skontrolujte nastavenia siete.
+
+ Problém komunikácie s proxy: {0}. Prosím skontrolujte nastavenia siete.
-
- Problém autentikácie SOCKS5: %1. Prosím skontrolujte nastavenia SOCKS5.
+
+ Problém autentikácie SOCKS5: {0}. Prosím skontrolujte nastavenia SOCKS5.
-
- Čas na vašom počítači, %1, možno nie je správny. Prosím, skontrolujete nastavenia.
+
+ Čas na vašom počítači, {0}, možno nie je správny. Prosím, skontrolujete nastavenia.
-
+
Meno % nenájdené.
-
- Dotaz prostredníctvom namecoinu zlyhal (%1)
+
+ Dotaz prostredníctvom namecoinu zlyhal ({0})
@@ -1409,18 +1409,18 @@ Priímcova požadovaná obtiažnosť: %1 a %2
-
- Meno %1 neobsahuje planté JSON dáta.
+
+ Meno {0} neobsahuje planté JSON dáta.
-
- Meno %1 nemá priradenú žiadnu adresu Bitmessage.
+
+ Meno {0} nemá priradenú žiadnu adresu Bitmessage.
-
- Úspech! Namecoind verzia %1 spustený.
+
+ Úspech! Namecoind verzia {0} spustený.
@@ -1479,53 +1479,53 @@ Vitajte v jednoduchom a bezpečnom Bitmessage
-
- Chyba: Bitmessage adresy začínajú s BM- Prosím skontrolujte adresu príjemcu %1
+
+ Chyba: Bitmessage adresy začínajú s BM- Prosím skontrolujte adresu príjemcu {0}
-
- Chyba: adresa príjemcu %1 nie je na správne napísaná alebo skopírovaná. Prosím skontrolujte ju.
+
+ Chyba: adresa príjemcu {0} nie je na správne napísaná alebo skopírovaná. Prosím skontrolujte ju.
-
- Chyba: adresa príjemcu %1 obsahuje neplatné znaky. Prosím skontrolujte ju.
+
+ Chyba: adresa príjemcu {0} obsahuje neplatné znaky. Prosím skontrolujte ju.
-
- Chyba: verzia adresy príjemcu %1 je príliš veľká. Buď musíte aktualizovať program Bitmessage alebo váš známy s vami žartuje.
+
+ Chyba: verzia adresy príjemcu {0} je príliš veľká. Buď musíte aktualizovať program Bitmessage alebo váš známy s vami žartuje.
-
- Chyba: niektoré údaje zakódované v adrese príjemcu %1 sú príliš krátke. Softér vášho známeho možno nefunguje správne.
+
+ Chyba: niektoré údaje zakódované v adrese príjemcu {0} sú príliš krátke. Softér vášho známeho možno nefunguje správne.
-
- Chyba: niektoré údaje zakódované v adrese príjemcu %1 sú príliš dlhé. Softvér vášho známeho možno nefunguje správne.
+
+ Chyba: niektoré údaje zakódované v adrese príjemcu {0} sú príliš dlhé. Softvér vášho známeho možno nefunguje správne.
-
- Chyba: niektoré údaje zakódované v adrese príjemcu %1 sú poškodené. Softvér vášho známeho možno nefunguje správne.
+
+ Chyba: niektoré údaje zakódované v adrese príjemcu {0} sú poškodené. Softvér vášho známeho možno nefunguje správne.
-
- Chyba: niečo s adresou príjemcu %1 je nie je v poriadku.
+
+ Chyba: niečo s adresou príjemcu {0} je nie je v poriadku.
-
- Chyba: %1
+
+ Chyba: {0}
-
- Od %1
+
+ Od {0}
@@ -1570,7 +1570,7 @@ Vitajte v jednoduchom a bezpečnom Bitmessage
- Zobraziť poslednú %1 hromadnú správu z tejto adresy.Zobraziť posledné %1 hromadné správy z tejto adresy.Zobraziť posledných %1 hromadných správ z tejto adresy.
+ Zobraziť poslednú {0} hromadnú správu z tejto adresy.Zobraziť posledné {0} hromadné správy z tejto adresy.Zobraziť posledných {0} hromadných správ z tejto adresy.
@@ -1617,8 +1617,8 @@ Vitajte v jednoduchom a bezpečnom Bitmessage
-
- Odkaz "%1" bude otvorený v prehliadači. Tento úkon môže predstavovať bezpečnostné riziko a Vás deanonymizovať, alebo vykonať škodlivú činnost. Ste si istý?
+
+ Odkaz "{0}" bude otvorený v prehliadači. Tento úkon môže predstavovať bezpečnostné riziko a Vás deanonymizovať, alebo vykonať škodlivú činnost. Ste si istý?
@@ -1953,8 +1953,8 @@ Predvoľba je pomocou generátora náhodných čísiel, ale deterministické adr
-
- Používate port TCP %1. (Možno zmeniť v nastaveniach).
+
+ Používate port TCP {0}. (Možno zmeniť v nastaveniach).
@@ -2006,28 +2006,28 @@ Predvoľba je pomocou generátora náhodných čísiel, ale deterministické adr
-
- Od spustenia %1
+
+ Od spustenia {0}
-
- Prijatých: %1/s Spolu: %2
+
+ Prijatých: {0}/s Spolu: {1}
-
- Odoslaných: %1/s Spolu: %2
+
+ Odoslaných: {0}/s Spolu: {1}
-
- Spojení spolu: %1
+
+ Spojení spolu: {0}
-
- Vyhľadaní v inventári za sekundu: %1
+
+ Vyhľadaní v inventári za sekundu: {0}
@@ -2192,8 +2192,8 @@ Predvoľba je pomocou generátora náhodných čísiel, ale deterministické adr
newchandialog
-
- Kanál %1 úspešne vytvorený/pripojený
+
+ Kanál {0} úspešne vytvorený/pripojený
diff --git a/src/translations/bitmessage_sv.ts b/src/translations/bitmessage_sv.ts
index 015546b3..8b854d5d 100644
--- a/src/translations/bitmessage_sv.ts
+++ b/src/translations/bitmessage_sv.ts
@@ -240,7 +240,7 @@ Please type the desired email address (including @mailchuck.com) below:
-
+
@@ -260,12 +260,12 @@ Please type the desired email address (including @mailchuck.com) below:
-
+
-
+
@@ -275,7 +275,7 @@ Please type the desired email address (including @mailchuck.com) below:
-
+
@@ -285,17 +285,17 @@ Please type the desired email address (including @mailchuck.com) below:
-
+
-
+
-
+
@@ -305,7 +305,7 @@ Please type the desired email address (including @mailchuck.com) below:
-
+
@@ -346,7 +346,7 @@ Please type the desired email address (including @mailchuck.com) below:
@@ -363,7 +363,7 @@ It is important that you back up this file.
@@ -429,7 +429,7 @@ It is important that you back up this file. Would you like to open the file now?
-
+
@@ -497,52 +497,52 @@ It is important that you back up this file. Would you like to open the file now?
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
@@ -557,7 +557,7 @@ It is important that you back up this file. Would you like to open the file now?
-
+
@@ -567,7 +567,7 @@ It is important that you back up this file. Would you like to open the file now?
-
+
@@ -702,7 +702,7 @@ It is important that you back up this file. Would you like to open the file now?
-
+
@@ -856,7 +856,7 @@ Are you sure you want to delete the channel?
-
+
@@ -1051,7 +1051,7 @@ Are you sure you want to delete the channel?
-
+
@@ -1066,47 +1066,47 @@ Are you sure you want to delete the channel?
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
@@ -1121,7 +1121,7 @@ Are you sure you want to delete the channel?
-
+
@@ -1141,12 +1141,12 @@ Are you sure you want to delete the channel?
-
+
-
+
@@ -1156,7 +1156,7 @@ Are you sure you want to delete the channel?
-
+
@@ -1181,7 +1181,7 @@ Are you sure you want to delete the channel?
-
+
@@ -1201,7 +1201,7 @@ Are you sure you want to delete the channel?
-
+
@@ -1213,17 +1213,17 @@ There is no required difficulty for version 2 addresses like this.
+Receiver's required difficulty: {0} and {1}
-
+
-
+
@@ -1233,7 +1233,7 @@ Receiver's required difficulty: %1 and %2
-
+
@@ -1248,12 +1248,12 @@ Receiver's required difficulty: %1 and %2
-
+
-
+
@@ -1601,27 +1601,27 @@ The 'Random Number' option is selected by default but deterministic ad
-
+
-
+
-
+
-
+
-
+
diff --git a/src/translations/bitmessage_zh_cn.ts b/src/translations/bitmessage_zh_cn.ts
index 474f8c6c..534e2f7a 100644
--- a/src/translations/bitmessage_zh_cn.ts
+++ b/src/translations/bitmessage_zh_cn.ts
@@ -352,8 +352,8 @@ Please type the desired email address (including @mailchuck.com) below:
-
- 您的地址中的一个, %1,是一个过时的版本1地址. 版本1地址已经不再受到支持了. 我们可以将它删除掉么?
+
+ 您的地址中的一个, {0},是一个过时的版本1地址. 版本1地址已经不再受到支持了. 我们可以将它删除掉么?
@@ -372,13 +372,13 @@ Please type the desired email address (including @mailchuck.com) below:
-
- 消息已经发送. 正在等待回执. 发送于 %1
+
+ 消息已经发送. 正在等待回执. 发送于 {0}
-
- 消息已经发送. 发送于 %1
+
+ 消息已经发送. 发送于 {0}
@@ -387,8 +387,8 @@ Please type the desired email address (including @mailchuck.com) below:
-
- 消息的回执已经收到于 %1
+
+ 消息的回执已经收到于 {0}
@@ -397,18 +397,18 @@ Please type the desired email address (including @mailchuck.com) below:
-
- 已经广播于 %1
+
+ 已经广播于 {0}
-
- 错误: 收件人要求的做工量大于我们的最大接受做工量。 %1
+
+ 错误: 收件人要求的做工量大于我们的最大接受做工量。 {0}
-
- 错误: 收件人的加密密钥是无效的。不能加密消息。 %1
+
+ 错误: 收件人的加密密钥是无效的。不能加密消息。 {0}
@@ -417,8 +417,8 @@ Please type the desired email address (including @mailchuck.com) below:
-
- 未知状态: %1 %2
+
+ 未知状态: {0} {1}
@@ -458,9 +458,9 @@ Please type the desired email address (including @mailchuck.com) below:
- 您可以通过编辑储存在 %1 的 keys.dat 来编辑密钥。备份这个文件十分重要。
+ 您可以通过编辑储存在 {0} 的 keys.dat 来编辑密钥。备份这个文件十分重要。
@@ -475,9 +475,9 @@ It is important that you back up this file.
- 您可以通过编辑储存在 %1 的 keys.dat 来编辑密钥。备份这个文件十分重要。您现在想打开这个文件么?(请在进行任何修改前关闭比特信)
+ 您可以通过编辑储存在 {0} 的 keys.dat 来编辑密钥。备份这个文件十分重要。您现在想打开这个文件么?(请在进行任何修改前关闭比特信)
@@ -541,7 +541,7 @@ It is important that you back up this file. Would you like to open the file now?
-
+
@@ -610,52 +610,52 @@ It is important that you back up this file. Would you like to open the file now?
-
+
您正在尝试发送的信息已超过 %1 个字节太长(最大为261644个字节),发送前请先缩短一些。
-
+
错误: 您的帐户没有在电子邮件网关注册。现在发送注册为%1, 注册正在处理请稍候重试发送.
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
@@ -670,8 +670,8 @@ It is important that you back up this file. Would you like to open the file now?
-
- 地址 %1 的地址版本号 %2 无法被比特信理解。也许您应该升级您的比特信到最新版本。
+
+ 地址 {0} 的地址版本号 {1} 无法被比特信理解。也许您应该升级您的比特信到最新版本。
@@ -680,8 +680,8 @@ It is important that you back up this file. Would you like to open the file now?
-
- 地址 %1 的节点流序号 %2 无法被比特信所理解。也许您应该升级您的比特信到最新版本。
+
+ 地址 {0} 的节点流序号 {1} 无法被比特信所理解。也许您应该升级您的比特信到最新版本。
@@ -815,8 +815,8 @@ It is important that you back up this file. Would you like to open the file now?
-
- 比特信无法找到您的地址 %1 ,也许您已经把它删掉了?
+
+ 比特信无法找到您的地址 {0} ,也许您已经把它删掉了?
@@ -973,7 +973,7 @@ Are you sure you want to delete the channel?
-
+
@@ -1168,7 +1168,7 @@ Are you sure you want to delete the channel?
-
+
缩放级别%1%
@@ -1183,48 +1183,48 @@ Are you sure you want to delete the channel?
-
+
-
- PyBitmessage的新版本可用: %1. 从https://github.com/Bitmessage/PyBitmessage/releases/latest下载
+
+ PyBitmessage的新版本可用: {0}. 从https://github.com/Bitmessage/PyBitmessage/releases/latest下载
-
- 等待PoW完成...%1%
+
+ 等待PoW完成...{0}%
-
- 关闭Pybitmessage ...%1%
+
+ 关闭Pybitmessage ...{0}%
-
- 等待要发送对象...%1%
+
+ 等待要发送对象...{0}%
-
- 保存设置...%1%
+
+ 保存设置...{0}%
-
- 关闭核心...%1%
+
+ 关闭核心...{0}%
-
- 停止通知...%1%
+
+ 停止通知...{0}%
-
- 关闭即将来临...%1%
+
+ 关闭即将来临...{0}%
@@ -1238,8 +1238,8 @@ Are you sure you want to delete the channel?
-
- 关闭PyBitmessage...%1%
+
+ 关闭PyBitmessage...{0}%
@@ -1258,13 +1258,13 @@ Are you sure you want to delete the channel?
-
- 生成%1个新地址.
+
+ 生成{0}个新地址.
-
- %1已经在'您的身份'. 不必重新添加.
+
+ {0}已经在'您的身份'. 不必重新添加.
@@ -1273,7 +1273,7 @@ Are you sure you want to delete the channel?
-
+
@@ -1298,8 +1298,8 @@ Are you sure you want to delete the channel?
-
- 广播发送%1
+
+ 广播发送{0}
@@ -1318,8 +1318,8 @@ Are you sure you want to delete the channel?
-
- 问题:对方是移动设备,并且对方的地址包含在此消息中,但是您的设置禁止了。 %1
+
+ 问题:对方是移动设备,并且对方的地址包含在此消息中,但是您的设置禁止了。 {0}
@@ -1331,19 +1331,19 @@ There is no required difficulty for version 2 addresses like this.
+Receiver's required difficulty: {0} and {1}
做必要的工作, 以发送短信.
-接收者的要求难度: %1与%2
+接收者的要求难度: {0}与{1}
-
- 问题: 由接收者(%1%2)要求的工作量比您愿意做的工作量來得更困难. %3
+
+ 问题: 由接收者({0}{1})要求的工作量比您愿意做的工作量來得更困难. {2}
-
- 问题: 您正在尝试将信息发送给自己或频道, 但您的加密密钥无法在keys.dat文件中找到. 无法加密信息. %1
+
+ 问题: 您正在尝试将信息发送给自己或频道, 但您的加密密钥无法在keys.dat文件中找到. 无法加密信息. {0}
@@ -1352,8 +1352,8 @@ Receiver's required difficulty: %1 and %2
-
- 信息发送. 等待确认. 已发送%1
+
+ 信息发送. 等待确认. 已发送{0}
@@ -1367,13 +1367,13 @@ Receiver's required difficulty: %1 and %2
-
- 发送公钥的请求. 等待回复. 请求在%1
+
+ 发送公钥的请求. 等待回复. 请求在{0}
-
- UPnP端口映射建立在端口%1
+
+ UPnP端口映射建立在端口{0}
@@ -1417,18 +1417,18 @@ Receiver's required difficulty: %1 and %2
-
- 名字%1未找到。
+
+ 名字{0}未找到。
-
- 域名币查询失败(%1)
+
+ 域名币查询失败({0})
-
- 未知的 Namecoin 界面类型: %1
+
+ 未知的 Namecoin 界面类型: {0}
@@ -1437,13 +1437,13 @@ Receiver's required difficulty: %1 and %2
-
- 名字%1没有关联比特信地址。
+
+ 名字{0}没有关联比特信地址。
-
- 成功!域名币系统%1运行中。
+
+ 成功!域名币系统{0}运行中。
@@ -1506,53 +1506,53 @@ Receiver's required difficulty: %1 and %2
-
- 错误:Bitmessage地址是以BM-开头的,请检查收信地址%1.
+
+ 错误:Bitmessage地址是以BM-开头的,请检查收信地址{0}.
-
- 错误:收信地址%1未填写或复制错误。请检查。
+
+ 错误:收信地址{0}未填写或复制错误。请检查。
-
- 错误:收信地址%1还有非法字符。请检查。
+
+ 错误:收信地址{0}还有非法字符。请检查。
-
- 错误:收信地址 %1 版本太高。要么您需要更新您的软件,要么对方需要降级 。
+
+ 错误:收信地址 {0} 版本太高。要么您需要更新您的软件,要么对方需要降级 。
-
- 错误:收信地址%1编码数据太短。可能对方使用的软件有问题。
+
+ 错误:收信地址{0}编码数据太短。可能对方使用的软件有问题。
-
+
错误:
-
- 错误:收信地址%1编码数据太长。可能对方使用的软件有问题。
+
+ 错误:收信地址{0}编码数据太长。可能对方使用的软件有问题。
-
- 错误:收信地址%1有问题。
+
+ 错误:收信地址{0}有问题。
-
- 错误:%1
+
+ 错误:{0}
-
- 来自 %1
+
+ 来自 {0}
@@ -1664,8 +1664,8 @@ Receiver's required difficulty: %1 and %2
-
- 此链接“%1”将在浏览器中打开。可能会有安全风险,可能会暴露您或下载恶意数据。确定吗?
+
+ 此链接“{0}”将在浏览器中打开。可能会有安全风险,可能会暴露您或下载恶意数据。确定吗?
@@ -1999,8 +1999,8 @@ The 'Random Number' option is selected by default but deterministic ad
-
- 您正在使用TCP端口 %1 。(可以在设置中修改)。
+
+ 您正在使用TCP端口 {0} 。(可以在设置中修改)。
@@ -2052,28 +2052,28 @@ The 'Random Number' option is selected by default but deterministic ad
-
- 自从%1启动
+
+ 自从{0}启动
-
- 下: %1/秒 总计: %2
+
+ 下: {0}/秒 总计: {1}
-
- 上: %1/秒 总计: %2
+
+ 上: {0}/秒 总计: {1}
-
- 总的连接数: %1
+
+ 总的连接数: {0}
-
- 每秒库存查询: %1
+
+ 每秒库存查询: {0}
@@ -2238,8 +2238,8 @@ The 'Random Number' option is selected by default but deterministic ad
newchandialog
-
- 成功创建或加入频道%1
+
+ 成功创建或加入频道{0}
diff --git a/src/translations/noarg.sh b/src/translations/noarg.sh
new file mode 100755
index 00000000..50d45d32
--- /dev/null
+++ b/src/translations/noarg.sh
@@ -0,0 +1,7 @@
+#!/bin/sh
+files=`ls *.ts`
+tmp_file=/tmp/noarg.sh.txt
+for file in $files; do
+ cat $file | sed 's/%1/{0}/g' | sed 's/%2/{1}/g' | sed 's/%3/{2}/g' > $tmp_file
+ mv $tmp_file $file
+done
diff --git a/src/translations/update.sh b/src/translations/update.sh
new file mode 100755
index 00000000..b3221486
--- /dev/null
+++ b/src/translations/update.sh
@@ -0,0 +1,2 @@
+#!/bin/sh
+lrelease-qt4 bitmessage.pro
diff --git a/src/unqstr.py b/src/unqstr.py
new file mode 100644
index 00000000..4c2994b1
--- /dev/null
+++ b/src/unqstr.py
@@ -0,0 +1,23 @@
+import sys
+import six
+
+def ustr(v):
+ if six.PY3:
+ if isinstance(v, str):
+ return v
+ elif isinstance(v, bytes):
+ return v.decode("utf-8", "replace")
+ else:
+ return str(v)
+ # assume six.PY2
+ if isinstance(v, unicode):
+ return v.encode("utf-8", "replace")
+ return str(v)
+
+def unic(v):
+ if six.PY3:
+ return v
+ # assume six.PY2
+ if isinstance(v, unicode):
+ return v
+ return unicode(v, "utf-8", "replace")
diff --git a/src/upnp.py b/src/upnp.py
index 42ff0c6d..28d1ee8a 100644
--- a/src/upnp.py
+++ b/src/upnp.py
@@ -4,13 +4,14 @@ Complete UPnP port forwarding implementation in separate thread.
Reference: http://mattscodecave.com/posts/using-python-and-upnp-to-forward-a-port
"""
-import httplib
+import six
+from six.moves import http_client as httplib
import re
import socket
import time
-import urllib2
+from six.moves.urllib.request import urlopen
from random import randint
-from urlparse import urlparse
+from six.moves.urllib.parse import urlparse
from xml.dom.minidom import Document # nosec B408
from defusedxml.minidom import parseString
@@ -93,12 +94,12 @@ class Router: # pylint: disable=old-style-class
self.address = address
- row = ssdpResponse.split('\r\n')
+ row = ssdpResponse.split(b'\r\n')
header = {}
for i in range(1, len(row)):
- part = row[i].split(': ')
+ part = row[i].split(b': ')
if len(part) == 2:
- header[part[0].lower()] = part[1]
+ header[part[0].decode("utf-8", "replace").lower()] = part[1].decode("utf-8", "replace")
try:
self.routerPath = urlparse(header['location'])
@@ -108,7 +109,7 @@ class Router: # pylint: disable=old-style-class
logger.error("UPnP: missing location header")
# get the profile xml file and read it into a variable
- directory = urllib2.urlopen(header['location']).read()
+ directory = urlopen(header['location']).read()
# create a DOM object that represents the `directory` document
dom = parseString(directory)
@@ -268,9 +269,12 @@ class uPnPThread(StoppableThread):
with knownnodes.knownNodesLock:
knownnodes.addKnownNode(
1, self_peer, is_self=True)
- queues.UISignalQueue.put(('updateStatusBar', tr._translate(
- "MainWindow", 'UPnP port mapping established on port %1'
- ).arg(str(self.extPort))))
+ queues.UISignalQueue.put((
+ 'updateStatusBar', tr._translate(
+ "MainWindow",
+ "UPnP port mapping established on port {0}"
+ ).format(self.extPort)
+ ))
break
except socket.timeout:
pass
@@ -315,7 +319,7 @@ class uPnPThread(StoppableThread):
try:
logger.debug("Sending UPnP query")
- self.sock.sendto(ssdpRequest, (uPnPThread.SSDP_ADDR, uPnPThread.SSDP_PORT))
+ self.sock.sendto(ssdpRequest.encode("utf8", "replace"), (uPnPThread.SSDP_ADDR, uPnPThread.SSDP_PORT))
except: # noqa:E722
logger.exception("UPnP send query failed")
diff --git a/stdeb.cfg b/stdeb.cfg
index 0d4cfbcb..f337a9fe 100644
--- a/stdeb.cfg
+++ b/stdeb.cfg
@@ -2,8 +2,8 @@
Package: pybitmessage
Section: net
Build-Depends: dh-python, libssl-dev, python-all-dev, python-setuptools, python-six
-Depends: openssl, python-setuptools
-Recommends: apparmor, python-msgpack, python-qt4, python-stem, tor
+Depends: openssl, python-setuptools, python-six
+Recommends: apparmor, python-msgpack, python-pyqt5, python-stem, tor
Suggests: python-pyopencl, python-jsonrpclib, python-defusedxml, python-qrcode
Suite: bionic
Setup-Env-Vars: DEB_BUILD_OPTIONS=nocheck
diff --git a/tests.py b/tests.py
index 713b25ef..0e3adaf1 100644
--- a/tests.py
+++ b/tests.py
@@ -3,11 +3,12 @@
import random # noseq
import sys
import unittest
+import six
def unittest_discover():
"""Explicit test suite creation"""
- if sys.hexversion >= 0x3000000:
+ if six.PY3:
from pybitmessage import pathmagic
pathmagic.setup()
loader = unittest.defaultTestLoader