Merge pull request #1365 from coffeedogs/final_code_quality_7

Changes based on style and lint checks. (final_code_quality_7)
This commit is contained in:
coffeedogs 2018-11-12 14:48:21 +00:00 committed by GitHub
commit a7a21e79ed
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 273 additions and 186 deletions

View File

@ -1,15 +1,20 @@
"""
src/bitmessageqt/messageview.py
===============================
"""
from PyQt4 import QtCore, QtGui from PyQt4 import QtCore, QtGui
import multiprocessing from safehtmlparser import SafeHTMLParser
import Queue
from urlparse import urlparse
from safehtmlparser import *
class MessageView(QtGui.QTextBrowser): class MessageView(QtGui.QTextBrowser):
"""Message content viewer class, can switch between plaintext and HTML"""
MODE_PLAIN = 0 MODE_PLAIN = 0
MODE_HTML = 1 MODE_HTML = 1
def __init__(self, parent = 0): def __init__(self, parent=0):
super(MessageView, self).__init__(parent) super(MessageView, self).__init__(parent)
self.mode = MessageView.MODE_PLAIN self.mode = MessageView.MODE_PLAIN
self.html = None self.html = None
@ -25,12 +30,14 @@ class MessageView(QtGui.QTextBrowser):
self.setWrappingWidth() self.setWrappingWidth()
def resizeEvent(self, event): def resizeEvent(self, event):
"""View resize event handler"""
super(MessageView, self).resizeEvent(event) super(MessageView, self).resizeEvent(event)
self.setWrappingWidth(event.size().width()) self.setWrappingWidth(event.size().width())
def mousePressEvent(self, event): def mousePressEvent(self, event):
#text = textCursor.block().text() """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: if self.mode == MessageView.MODE_PLAIN:
self.showHTML() self.showHTML()
else: else:
@ -39,19 +46,24 @@ class MessageView(QtGui.QTextBrowser):
super(MessageView, self).mousePressEvent(event) super(MessageView, self).mousePressEvent(event)
def wheelEvent(self, event): def wheelEvent(self, event):
"""Mouse wheel scroll event handler"""
# super will actually automatically take care of zooming # super will actually automatically take care of zooming
super(MessageView, self).wheelEvent(event) super(MessageView, self).wheelEvent(event)
if (QtGui.QApplication.queryKeyboardModifiers() & QtCore.Qt.ControlModifier) == QtCore.Qt.ControlModifier and event.orientation() == QtCore.Qt.Vertical: if (QtGui.QApplication.queryKeyboardModifiers() &
QtCore.Qt.ControlModifier) == QtCore.Qt.ControlModifier and event.orientation() == QtCore.Qt.Vertical:
zoom = self.currentFont().pointSize() * 100 / self.defaultFontPointSize zoom = self.currentFont().pointSize() * 100 / self.defaultFontPointSize
QtGui.QApplication.activeWindow().statusBar().showMessage(QtGui.QApplication.translate("MainWindow", "Zoom level %1%").arg(str(zoom))) QtGui.QApplication.activeWindow().statusBar().showMessage(
QtGui.QApplication.translate("MainWindow", "Zoom level %1%").arg(str(zoom)))
def setWrappingWidth(self, width=None): def setWrappingWidth(self, width=None):
"""Set word-wrapping width"""
self.setLineWrapMode(QtGui.QTextEdit.FixedPixelWidth) self.setLineWrapMode(QtGui.QTextEdit.FixedPixelWidth)
if width is None: if width is None:
width = self.width() width = self.width()
self.setLineWrapColumnOrWidth(width) self.setLineWrapColumnOrWidth(width)
def confirmURL(self, link): def confirmURL(self, link):
"""Show a dialog requesting URL opening confirmation"""
if link.scheme() == "mailto": if link.scheme() == "mailto":
window = QtGui.QApplication.activeWindow() window = QtGui.QApplication.activeWindow()
window.ui.lineEditTo.setText(link.path()) window.ui.lineEditTo.setText(link.path())
@ -68,35 +80,39 @@ class MessageView(QtGui.QTextBrowser):
) )
window.ui.textEditMessage.setFocus() window.ui.textEditMessage.setFocus()
return return
reply = QtGui.QMessageBox.warning(self, reply = QtGui.QMessageBox.warning(
QtGui.QApplication.translate("MessageView", "Follow external link"), self,
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.QApplication.translate(
QtGui.QMessageBox.Yes, QtGui.QMessageBox.No) "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: if reply == QtGui.QMessageBox.Yes:
QtGui.QDesktopServices.openUrl(link) QtGui.QDesktopServices.openUrl(link)
def loadResource (self, restype, name): def loadResource(self, restype, name):
if restype == QtGui.QTextDocument.ImageResource and name.scheme() == "bmmsg": """
pass Callback for loading referenced objects, such as an image. For security reasons at the moment doesn't do
# QImage correctImage; anything)
# lookup the correct QImage from a cache """
# return QVariant::fromValue(correctImage); pass
# elif restype == QtGui.QTextDocument.HtmlResource:
# elif restype == QtGui.QTextDocument.ImageResource:
# elif restype == QtGui.QTextDocument.StyleSheetResource:
# elif restype == QtGui.QTextDocument.UserResource:
else:
pass
# by default, this will interpret it as a local file
# QtGui.QTextBrowser.loadResource(restype, name)
def lazyRender(self): def lazyRender(self):
"""
Partially render a message. This is to avoid UI freezing when loading huge messages. It continues loading as
you scroll down.
"""
if self.rendering: if self.rendering:
return return
self.rendering = True self.rendering = True
position = self.verticalScrollBar().value() position = self.verticalScrollBar().value()
cursor = QtGui.QTextCursor(self.document()) 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 startpos = self.outpos
self.outpos += 10240 self.outpos += 10240
# find next end of tag # find next end of tag
@ -110,25 +126,31 @@ class MessageView(QtGui.QTextBrowser):
self.rendering = False self.rendering = False
def showPlain(self): def showPlain(self):
"""Render message as plain text."""
self.mode = MessageView.MODE_PLAIN self.mode = MessageView.MODE_PLAIN
out = self.html.raw out = self.html.raw
if self.html.has_html: if self.html.has_html:
out = "<div align=\"center\" style=\"text-decoration: underline;\"><b>" + unicode(QtGui.QApplication.translate("MessageView", "HTML detected, click here to display")) + "</b></div><br/>" + out out = "<div align=\"center\" style=\"text-decoration: underline;\"><b>" + unicode(
QtGui.QApplication.translate(
"MessageView", "HTML detected, click here to display")) + "</b></div><br/>" + out
self.out = out self.out = out
self.outpos = 0 self.outpos = 0
self.setHtml("") self.setHtml("")
self.lazyRender() self.lazyRender()
def showHTML(self): def showHTML(self):
"""Render message as HTML"""
self.mode = MessageView.MODE_HTML self.mode = MessageView.MODE_HTML
out = self.html.sanitised out = self.html.sanitised
out = "<div align=\"center\" style=\"text-decoration: underline;\"><b>" + unicode(QtGui.QApplication.translate("MessageView", "Click here to disable HTML")) + "</b></div><br/>" + out out = "<div align=\"center\" style=\"text-decoration: underline;\"><b>" + unicode(
QtGui.QApplication.translate("MessageView", "Click here to disable HTML")) + "</b></div><br/>" + out
self.out = out self.out = out
self.outpos = 0 self.outpos = 0
self.setHtml("") self.setHtml("")
self.lazyRender() self.lazyRender()
def setContent(self, data): def setContent(self, data):
"""Set message content from argument"""
self.html = SafeHTMLParser() self.html = SafeHTMLParser()
self.html.reset() self.html.reset()
self.html.reset_safe() self.html.reset_safe()

View File

@ -1,20 +1,27 @@
from PyQt4 import QtCore, QtGui """
import time src/bitmessageqt/networkstatus.py
import shared =================================
"""
import time
from PyQt4 import QtCore, QtGui
from tr import _translate
from inventory import Inventory
import knownnodes import knownnodes
import l10n import l10n
import network.stats import network.stats
from retranslateui import RetranslateMixin import shared
from uisignaler import UISignaler
import widgets import widgets
from inventory import Inventory
from network.connectionpool import BMConnectionPool from network.connectionpool import BMConnectionPool
from retranslateui import RetranslateMixin
from tr import _translate
from uisignaler import UISignaler
class NetworkStatus(QtGui.QWidget, RetranslateMixin): class NetworkStatus(QtGui.QWidget, RetranslateMixin):
"""Network status tab"""
def __init__(self, parent=None): def __init__(self, parent=None):
super(NetworkStatus, self).__init__(parent) super(NetworkStatus, self).__init__(parent)
widgets.load('networkstatus.ui', self) widgets.load('networkstatus.ui', self)
@ -31,6 +38,7 @@ class NetworkStatus(QtGui.QWidget, RetranslateMixin):
l10n.formatTimestamp(self.startup))) l10n.formatTimestamp(self.startup)))
self.UISignalThread = UISignaler.get() self.UISignalThread = UISignaler.get()
# pylint: disable=no-member
QtCore.QObject.connect(self.UISignalThread, QtCore.SIGNAL( QtCore.QObject.connect(self.UISignalThread, QtCore.SIGNAL(
"updateNumberOfMessagesProcessed()"), self.updateNumberOfMessagesProcessed) "updateNumberOfMessagesProcessed()"), self.updateNumberOfMessagesProcessed)
QtCore.QObject.connect(self.UISignalThread, QtCore.SIGNAL( QtCore.QObject.connect(self.UISignalThread, QtCore.SIGNAL(
@ -42,57 +50,108 @@ class NetworkStatus(QtGui.QWidget, RetranslateMixin):
self.timer = QtCore.QTimer() self.timer = QtCore.QTimer()
QtCore.QObject.connect( QtCore.QObject.connect(self.timer, QtCore.SIGNAL("timeout()"), self.runEveryTwoSeconds)
self.timer, QtCore.SIGNAL("timeout()"), self.runEveryTwoSeconds) # pylint: enable=no-member
def startUpdate(self): def startUpdate(self):
"""Start a timer to update counters every 2 seconds"""
Inventory().numberOfInventoryLookupsPerformed = 0 Inventory().numberOfInventoryLookupsPerformed = 0
self.runEveryTwoSeconds() self.runEveryTwoSeconds()
self.timer.start(2000) # milliseconds self.timer.start(2000) # milliseconds
def stopUpdate(self): def stopUpdate(self):
"""Stop counter update timer"""
self.timer.stop() self.timer.stop()
def formatBytes(self, num): def formatBytes(self, num):
for x in [_translate("networkstatus", "byte(s)", None, QtCore.QCoreApplication.CodecForTr, num), "kB", "MB", "GB"]: """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",
]:
if num < 1000.0: if num < 1000.0:
return "%3.0f %s" % (num, x) return "%3.0f %s" % (num, x)
num /= 1000.0 num /= 1000.0
return "%3.0f %s" % (num, 'TB') return "%3.0f %s" % (num, 'TB')
def formatByteRate(self, num): def formatByteRate(self, num):
"""Format transfer speed in kB/s"""
# pylint: disable=no-self-use
num /= 1000 num /= 1000
return "%4.0f kB" % num return "%4.0f kB" % num
def updateNumberOfObjectsToBeSynced(self): def updateNumberOfObjectsToBeSynced(self):
self.labelSyncStatus.setText(_translate("networkstatus", "Object(s) to be synced: %n", None, QtCore.QCoreApplication.CodecForTr, network.stats.pendingDownload() + network.stats.pendingUpload())) """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()))
def updateNumberOfMessagesProcessed(self): def updateNumberOfMessagesProcessed(self):
"""Update the counter for number of processed messages"""
self.updateNumberOfObjectsToBeSynced() self.updateNumberOfObjectsToBeSynced()
self.labelMessageCount.setText(_translate( self.labelMessageCount.setText(
"networkstatus", "Processed %n person-to-person message(s).", None, QtCore.QCoreApplication.CodecForTr, shared.numberOfMessagesProcessed)) _translate(
"networkstatus",
"Processed %n person-to-person message(s).",
None,
QtCore.QCoreApplication.CodecForTr,
shared.numberOfMessagesProcessed))
def updateNumberOfBroadcastsProcessed(self): def updateNumberOfBroadcastsProcessed(self):
"""Update the counter for the number of processed broadcasts"""
self.updateNumberOfObjectsToBeSynced() self.updateNumberOfObjectsToBeSynced()
self.labelBroadcastCount.setText(_translate( self.labelBroadcastCount.setText(
"networkstatus", "Processed %n broadcast message(s).", None, QtCore.QCoreApplication.CodecForTr, shared.numberOfBroadcastsProcessed)) _translate(
"networkstatus",
"Processed %n broadcast message(s).",
None,
QtCore.QCoreApplication.CodecForTr,
shared.numberOfBroadcastsProcessed))
def updateNumberOfPubkeysProcessed(self): def updateNumberOfPubkeysProcessed(self):
"""Update the counter for the number of processed pubkeys"""
self.updateNumberOfObjectsToBeSynced() self.updateNumberOfObjectsToBeSynced()
self.labelPubkeyCount.setText(_translate( self.labelPubkeyCount.setText(
"networkstatus", "Processed %n public key(s).", None, QtCore.QCoreApplication.CodecForTr, shared.numberOfPubkeysProcessed)) _translate(
"networkstatus",
"Processed %n public key(s).",
None,
QtCore.QCoreApplication.CodecForTr,
shared.numberOfPubkeysProcessed))
def updateNumberOfBytes(self): def updateNumberOfBytes(self):
""" """
This function is run every two seconds, so we divide the rate of bytes This function is run every two seconds, so we divide the rate of bytes
sent and received by 2. sent and received by 2.
""" """
self.labelBytesRecvCount.setText(_translate( self.labelBytesRecvCount.setText(
"networkstatus", "Down: %1/s Total: %2").arg(self.formatByteRate(network.stats.downloadSpeed()), self.formatBytes(network.stats.receivedBytes()))) _translate(
self.labelBytesSentCount.setText(_translate( "networkstatus",
"networkstatus", "Up: %1/s Total: %2").arg(self.formatByteRate(network.stats.uploadSpeed()), self.formatBytes(network.stats.sentBytes()))) "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())))
def updateNetworkStatusTab(self, outbound, add, destination): def updateNetworkStatusTab(self, outbound, add, destination):
"""Add or remove an entry to the list of connected peers"""
# pylint: disable=too-many-branches,undefined-variable
if outbound: if outbound:
try: try:
c = BMConnectionPool().outboundConnections[destination] c = BMConnectionPool().outboundConnections[destination]
@ -111,33 +170,39 @@ class NetworkStatus(QtGui.QWidget, RetranslateMixin):
self.tableWidgetConnectionCount.setUpdatesEnabled(False) self.tableWidgetConnectionCount.setUpdatesEnabled(False)
self.tableWidgetConnectionCount.setSortingEnabled(False) self.tableWidgetConnectionCount.setSortingEnabled(False)
if add: if add:
self.tableWidgetConnectionCount.insertRow(0) self.tableWidgetConnectionCount.insertRow(0)
self.tableWidgetConnectionCount.setItem(0, 0, self.tableWidgetConnectionCount.setItem(
0, 0,
QtGui.QTableWidgetItem("%s:%i" % (destination.host, destination.port)) QtGui.QTableWidgetItem("%s:%i" % (destination.host, destination.port))
) )
self.tableWidgetConnectionCount.setItem(0, 2, self.tableWidgetConnectionCount.setItem(
0, 2,
QtGui.QTableWidgetItem("%s" % (c.userAgent)) QtGui.QTableWidgetItem("%s" % (c.userAgent))
) )
self.tableWidgetConnectionCount.setItem(0, 3, self.tableWidgetConnectionCount.setItem(
0, 3,
QtGui.QTableWidgetItem("%s" % (c.tlsVersion)) QtGui.QTableWidgetItem("%s" % (c.tlsVersion))
) )
self.tableWidgetConnectionCount.setItem(0, 4, self.tableWidgetConnectionCount.setItem(
QtGui.QTableWidgetItem("%s" % (",".join(map(str,c.streams)))) 0, 4,
) QtGui.QTableWidgetItem("%s" % (",".join(map(str, c.streams))))
)
try: try:
# FIXME hard coded stream no # .. todo:: FIXME: hard coded stream no
rating = "%.1f" % (knownnodes.knownNodes[1][destination]['rating']) rating = "%.1f" % (knownnodes.knownNodes[1][destination]['rating'])
except KeyError: except KeyError:
rating = "-" rating = "-"
self.tableWidgetConnectionCount.setItem(0, 1, self.tableWidgetConnectionCount.setItem(
0, 1,
QtGui.QTableWidgetItem("%s" % (rating)) QtGui.QTableWidgetItem("%s" % (rating))
) )
if outbound: if outbound:
brush = QtGui.QBrush(QtGui.QColor("yellow"), QtCore.Qt.SolidPattern) brush = QtGui.QBrush(QtGui.QColor("yellow"), QtCore.Qt.SolidPattern)
else: else:
brush = QtGui.QBrush(QtGui.QColor("green"), QtCore.Qt.SolidPattern) brush = QtGui.QBrush(QtGui.QColor("green"), QtCore.Qt.SolidPattern)
for j in (range(1)): for j in range(1):
self.tableWidgetConnectionCount.item(0, j).setBackground(brush) self.tableWidgetConnectionCount.item(0, j).setBackground(brush)
self.tableWidgetConnectionCount.item(0, 0).setData(QtCore.Qt.UserRole, destination) self.tableWidgetConnectionCount.item(0, 0).setData(QtCore.Qt.UserRole, destination)
self.tableWidgetConnectionCount.item(0, 1).setData(QtCore.Qt.UserRole, outbound) self.tableWidgetConnectionCount.item(0, 1).setData(QtCore.Qt.UserRole, outbound)
@ -148,11 +213,16 @@ class NetworkStatus(QtGui.QWidget, RetranslateMixin):
if self.tableWidgetConnectionCount.item(i, 1).data(QtCore.Qt.UserRole).toPyObject() == outbound: if self.tableWidgetConnectionCount.item(i, 1).data(QtCore.Qt.UserRole).toPyObject() == outbound:
self.tableWidgetConnectionCount.removeRow(i) self.tableWidgetConnectionCount.removeRow(i)
break break
self.tableWidgetConnectionCount.setUpdatesEnabled(True) self.tableWidgetConnectionCount.setUpdatesEnabled(True)
self.tableWidgetConnectionCount.setSortingEnabled(True) self.tableWidgetConnectionCount.setSortingEnabled(True)
self.labelTotalConnections.setText(_translate( self.labelTotalConnections.setText(
"networkstatus", "Total Connections: %1").arg(str(self.tableWidgetConnectionCount.rowCount()))) _translate(
# 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. "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 shared.statusIconColor == 'red': if self.tableWidgetConnectionCount.rowCount() and shared.statusIconColor == 'red':
self.window().setStatusIcon('yellow') self.window().setStatusIcon('yellow')
elif self.tableWidgetConnectionCount.rowCount() == 0 and shared.statusIconColor != "red": elif self.tableWidgetConnectionCount.rowCount() == 0 and shared.statusIconColor != "red":
@ -160,8 +230,9 @@ class NetworkStatus(QtGui.QWidget, RetranslateMixin):
# timer driven # timer driven
def runEveryTwoSeconds(self): def runEveryTwoSeconds(self):
self.labelLookupsPerSecond.setText(_translate( """Updates counters, runs every 2 seconds if the timer is running"""
"networkstatus", "Inventory lookups per second: %1").arg(str(Inventory().numberOfInventoryLookupsPerformed/2))) self.labelLookupsPerSecond.setText(_translate("networkstatus", "Inventory lookups per second: %1").arg(
str(Inventory().numberOfInventoryLookupsPerformed / 2)))
Inventory().numberOfInventoryLookupsPerformed = 0 Inventory().numberOfInventoryLookupsPerformed = 0
self.updateNumberOfBytes() self.updateNumberOfBytes()
self.updateNumberOfObjectsToBeSynced() self.updateNumberOfObjectsToBeSynced()

View File

@ -1,40 +1,41 @@
"""
src/class_singleWorker.py
=========================
"""
# pylint: disable=protected-access,too-many-branches,too-many-statements,no-self-use,too-many-lines,too-many-locals
from __future__ import division from __future__ import division
import time
import threading
import hashlib import hashlib
from struct import pack import threading
# used when the API must execute an outside program import time
from subprocess import call # nosec
from binascii import hexlify, unhexlify from binascii import hexlify, unhexlify
from struct import pack
from subprocess import call # nosec
import tr import defaults
import helper_inbox
import helper_msgcoding
import helper_random
import highlevelcrypto
import l10n import l10n
import proofofwork
import protocol import protocol
import queues import queues
import state
import shared import shared
import defaults import state
import highlevelcrypto import tr
import proofofwork from addresses import calculateInventoryHash, decodeAddress, decodeVarint, encodeVarint
import helper_inbox
import helper_random
import helper_msgcoding
from bmconfigparser import BMConfigParser from bmconfigparser import BMConfigParser
from debug import logger from debug import logger
from inventory import Inventory from helper_sql import sqlExecute, sqlQuery
from addresses import (
decodeAddress, encodeVarint, decodeVarint, calculateInventoryHash
)
# from helper_generic import addDataPadding
from helper_threading import StoppableThread from helper_threading import StoppableThread
from helper_sql import sqlQuery, sqlExecute from inventory import Inventory
# This thread, of which there is only one, does the heavy lifting:
# calculating POWs.
def sizeof_fmt(num, suffix='h/s'): def sizeof_fmt(num, suffix='h/s'):
"""Format hashes per seconds nicely (SI prefix)"""
for unit in ['', 'k', 'M', 'G', 'T', 'P', 'E', 'Z']: for unit in ['', 'k', 'M', 'G', 'T', 'P', 'E', 'Z']:
if abs(num) < 1000.0: if abs(num) < 1000.0:
return "%3.1f%s%s" % (num, unit, suffix) return "%3.1f%s%s" % (num, unit, suffix)
@ -43,14 +44,16 @@ def sizeof_fmt(num, suffix='h/s'):
class singleWorker(threading.Thread, StoppableThread): class singleWorker(threading.Thread, StoppableThread):
"""Thread for performing PoW"""
def __init__(self): def __init__(self):
# QThread.__init__(self, parent)
threading.Thread.__init__(self, name="singleWorker") threading.Thread.__init__(self, name="singleWorker")
self.initStop() self.initStop()
proofofwork.init() proofofwork.init()
def stopThread(self): def stopThread(self):
"""Signal through the queue that the thread should be stopped"""
try: try:
queues.workerQueue.put(("stopThread", "data")) queues.workerQueue.put(("stopThread", "data"))
except: except:
@ -58,6 +61,7 @@ class singleWorker(threading.Thread, StoppableThread):
super(singleWorker, self).stopThread() super(singleWorker, self).stopThread()
def run(self): def run(self):
# pylint: disable=attribute-defined-outside-init
while not state.sqlReady and state.shutdown == 0: while not state.sqlReady and state.shutdown == 0:
self.stop.wait(2) self.stop.wait(2)
@ -96,12 +100,12 @@ class singleWorker(threading.Thread, StoppableThread):
'''SELECT ackdata FROM sent WHERE status = 'msgsent' ''') '''SELECT ackdata FROM sent WHERE status = 'msgsent' ''')
for row in queryreturn: for row in queryreturn:
ackdata, = row ackdata, = row
logger.info('Watching for ackdata ' + hexlify(ackdata)) logger.info('Watching for ackdata %s', hexlify(ackdata))
shared.ackdataForWhichImWatching[ackdata] = 0 shared.ackdataForWhichImWatching[ackdata] = 0
# Fix legacy (headerless) watched ackdata to include header # Fix legacy (headerless) watched ackdata to include header
for oldack in shared.ackdataForWhichImWatching.keys(): for oldack in shared.ackdataForWhichImWatching:
if (len(oldack) == 32): if len(oldack) == 32:
# attach legacy header, always constant (msg/1/1) # attach legacy header, always constant (msg/1/1)
newack = '\x00\x00\x00\x02\x01\x01' + oldack newack = '\x00\x00\x00\x02\x01\x01' + oldack
shared.ackdataForWhichImWatching[newack] = 0 shared.ackdataForWhichImWatching[newack] = 0
@ -226,19 +230,9 @@ class singleWorker(threading.Thread, StoppableThread):
# inventoryHash = calculateInventoryHash(payload) # inventoryHash = calculateInventoryHash(payload)
return payload return payload
# This function also broadcasts out the pubkey message
# once it is done with the POW
def doPOWForMyV2Pubkey(self, adressHash): def doPOWForMyV2Pubkey(self, adressHash):
""" 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 # Look up my stream number based on my address hash
"""configSections = shared.config.addresses()
for addressInKeysFile in configSections:
if addressInKeysFile != 'bitmessagesettings':
status, addressVersionNumber, streamNumber, \
hashFromThisParticularAddress = \
decodeAddress(addressInKeysFile)
if hash == hashFromThisParticularAddress:
myAddress = addressInKeysFile
break"""
myAddress = shared.myAddressesByHash[adressHash] myAddress = shared.myAddressesByHash[adressHash]
# status # status
_, addressVersionNumber, streamNumber, adressHash = decodeAddress(myAddress) _, addressVersionNumber, streamNumber, adressHash = decodeAddress(myAddress)
@ -289,11 +283,12 @@ class singleWorker(threading.Thread, StoppableThread):
# before this finished. # before this finished.
pass pass
# 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".
def sendOutOrStoreMyV3Pubkey(self, adressHash): 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".
"""
try: try:
myAddress = shared.myAddressesByHash[adressHash] myAddress = shared.myAddressesByHash[adressHash]
except: except:
@ -357,7 +352,7 @@ class singleWorker(threading.Thread, StoppableThread):
Inventory()[inventoryHash] = ( Inventory()[inventoryHash] = (
objectType, streamNumber, payload, embeddedTime, '') objectType, streamNumber, payload, embeddedTime, '')
logger.info('broadcasting inv with hash: ' + hexlify(inventoryHash)) logger.info('broadcasting inv with hash: %s', hexlify(inventoryHash))
queues.invQueue.put((streamNumber, inventoryHash)) queues.invQueue.put((streamNumber, inventoryHash))
queues.UISignalQueue.put(('updateStatusBar', '')) queues.UISignalQueue.put(('updateStatusBar', ''))
@ -370,9 +365,12 @@ class singleWorker(threading.Thread, StoppableThread):
# before this finished. # before this finished.
pass pass
# If this isn't a chan address, this function assembles
# the pubkey data, does the necessary POW and sends it out.
def sendOutOrStoreMyV4Pubkey(self, myAddress): 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.
"""
if not BMConfigParser().has_section(myAddress): if not BMConfigParser().has_section(myAddress):
# The address has been deleted. # The address has been deleted.
return return
@ -444,7 +442,7 @@ class singleWorker(threading.Thread, StoppableThread):
doubleHashOfAddressData[32:] doubleHashOfAddressData[32:]
) )
logger.info('broadcasting inv with hash: ' + hexlify(inventoryHash)) logger.info('broadcasting inv with hash: %s', hexlify(inventoryHash))
queues.invQueue.put((streamNumber, inventoryHash)) queues.invQueue.put((streamNumber, inventoryHash))
queues.UISignalQueue.put(('updateStatusBar', '')) queues.UISignalQueue.put(('updateStatusBar', ''))
@ -459,6 +457,7 @@ class singleWorker(threading.Thread, StoppableThread):
) )
def sendBroadcast(self): def sendBroadcast(self):
"""Send a broadcast-type object (assemble the object, perform PoW and put it to the inv announcement queue)"""
# Reset just in case # Reset just in case
sqlExecute( sqlExecute(
'''UPDATE sent SET status='broadcastqueued' ''' '''UPDATE sent SET status='broadcastqueued' '''
@ -627,6 +626,8 @@ class singleWorker(threading.Thread, StoppableThread):
) )
def sendMsg(self): def sendMsg(self):
"""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 # Reset just in case
sqlExecute( sqlExecute(
'''UPDATE sent SET status='msgqueued' ''' '''UPDATE sent SET status='msgqueued' '''
@ -740,10 +741,8 @@ class singleWorker(threading.Thread, StoppableThread):
# object associated with the tag for this toAddress. # object associated with the tag for this toAddress.
if toAddressVersionNumber >= 4: if toAddressVersionNumber >= 4:
doubleHashOfToAddressData = hashlib.sha512( doubleHashOfToAddressData = hashlib.sha512(
hashlib.sha512(encodeVarint( hashlib.sha512(
toAddressVersionNumber) + encodeVarint(toAddressVersionNumber) + encodeVarint(toStreamNumber) + toRipe
encodeVarint(toStreamNumber) +
toRipe
).digest() ).digest()
).digest() ).digest()
# The first half of the sha512 hash. # The first half of the sha512 hash.
@ -834,7 +833,7 @@ class singleWorker(threading.Thread, StoppableThread):
queryreturn = sqlQuery( queryreturn = sqlQuery(
'SELECT transmitdata FROM pubkeys WHERE address=?', 'SELECT transmitdata FROM pubkeys WHERE address=?',
toaddress) toaddress)
for row in queryreturn: for row in queryreturn: # pylint: disable=redefined-outer-name
pubkeyPayload, = row pubkeyPayload, = row
# The pubkey message is stored with the following items # The pubkey message is stored with the following items
@ -939,40 +938,43 @@ class singleWorker(threading.Thread, StoppableThread):
requiredAverageProofOfWorkNonceTrialsPerByte, requiredAverageProofOfWorkNonceTrialsPerByte,
requiredPayloadLengthExtraBytes requiredPayloadLengthExtraBytes
) )
queues.UISignalQueue.put((
'updateSentItemStatusByAckdata', ( queues.UISignalQueue.put(
ackdata, (
tr._translate( 'updateSentItemStatusByAckdata',
"MainWindow", (
"Doing work necessary to send message.\n" ackdata,
"Receiver\'s required difficulty: %1" tr._translate(
" and %2" "MainWindow",
).arg(str(float( "Doing work necessary to send message.\n"
requiredAverageProofOfWorkNonceTrialsPerByte) / "Receiver\'s required difficulty: %1"
defaults.networkDefaultProofOfWorkNonceTrialsPerByte " and %2"
)).arg(str(float( ).arg(
requiredPayloadLengthExtraBytes) / str(
defaults.networkDefaultPayloadLengthExtraBytes float(requiredAverageProofOfWorkNonceTrialsPerByte) /
))))) defaults.networkDefaultProofOfWorkNonceTrialsPerByte
)
).arg(
str(
float(requiredPayloadLengthExtraBytes) /
defaults.networkDefaultPayloadLengthExtraBytes
)
)
)
)
)
if status != 'forcepow': if status != 'forcepow':
if (requiredAverageProofOfWorkNonceTrialsPerByte maxacceptablenoncetrialsperbyte = BMConfigParser().getint(
> BMConfigParser().getint( 'bitmessagesettings', 'maxacceptablenoncetrialsperbyte')
'bitmessagesettings', maxacceptablepayloadlengthextrabytes = BMConfigParser().getint(
'maxacceptablenoncetrialsperbyte' 'bitmessagesettings', 'maxacceptablepayloadlengthextrabytes')
) and cond1 = maxacceptablenoncetrialsperbyte and \
BMConfigParser().getint( requiredAverageProofOfWorkNonceTrialsPerByte > maxacceptablenoncetrialsperbyte
'bitmessagesettings', cond2 = maxacceptablepayloadlengthextrabytes and \
'maxacceptablenoncetrialsperbyte' requiredPayloadLengthExtraBytes > maxacceptablepayloadlengthextrabytes
) != 0) or (
requiredPayloadLengthExtraBytes if cond1 or cond2:
> BMConfigParser().getint(
'bitmessagesettings',
'maxacceptablepayloadlengthextrabytes'
) and
BMConfigParser().getint(
'bitmessagesettings',
'maxacceptablepayloadlengthextrabytes'
) != 0):
# The demanded difficulty is more than # The demanded difficulty is more than
# we are willing to do. # we are willing to do.
sqlExecute( sqlExecute(
@ -988,19 +990,15 @@ class singleWorker(threading.Thread, StoppableThread):
" the recipient (%1 and %2) is" " the recipient (%1 and %2) is"
" more difficult than you are" " more difficult than you are"
" willing to do. %3" " willing to do. %3"
).arg(str(float( ).arg(str(float(requiredAverageProofOfWorkNonceTrialsPerByte) /
requiredAverageProofOfWorkNonceTrialsPerByte) defaults.networkDefaultProofOfWorkNonceTrialsPerByte)).arg(
/ defaults.networkDefaultProofOfWorkNonceTrialsPerByte str(float(requiredPayloadLengthExtraBytes) /
)).arg(str(float( defaults.networkDefaultPayloadLengthExtraBytes)).arg(
requiredPayloadLengthExtraBytes) l10n.formatTimestamp()))))
/ defaults.networkDefaultPayloadLengthExtraBytes
)).arg(l10n.formatTimestamp()))
))
continue continue
else: # if we are sending a message to ourselves or a chan.. else: # if we are sending a message to ourselves or a chan..
logger.info('Sending a message.') logger.info('Sending a message.')
logger.debug( logger.debug('First 150 characters of message: %r', message[:150])
'First 150 characters of message: %r', message[:150])
behaviorBitfield = protocol.getBitfield(fromaddress) behaviorBitfield = protocol.getBitfield(fromaddress)
try: try:
@ -1199,16 +1197,14 @@ class singleWorker(threading.Thread, StoppableThread):
Inventory()[inventoryHash] = ( Inventory()[inventoryHash] = (
objectType, toStreamNumber, encryptedPayload, embeddedTime, '') objectType, toStreamNumber, encryptedPayload, embeddedTime, '')
if BMConfigParser().has_section(toaddress) or \ if BMConfigParser().has_section(toaddress) or \
not protocol.checkBitfield( not protocol.checkBitfield(behaviorBitfield, protocol.BITFIELD_DOESACK):
behaviorBitfield, protocol.BITFIELD_DOESACK):
queues.UISignalQueue.put(( queues.UISignalQueue.put((
'updateSentItemStatusByAckdata', ( 'updateSentItemStatusByAckdata', (
ackdata, ackdata,
tr._translate( tr._translate(
"MainWindow", "MainWindow",
"Message sent. Sent at %1" "Message sent. Sent at %1"
).arg(l10n.formatTimestamp())) ).arg(l10n.formatTimestamp()))))
))
else: else:
# not sending to a chan or one of my addresses # not sending to a chan or one of my addresses
queues.UISignalQueue.put(( queues.UISignalQueue.put((
@ -1229,8 +1225,7 @@ class singleWorker(threading.Thread, StoppableThread):
# Update the sent message in the sent table with the # Update the sent message in the sent table with the
# necessary information. # necessary information.
if BMConfigParser().has_section(toaddress) or \ if BMConfigParser().has_section(toaddress) or \
not protocol.checkBitfield( not protocol.checkBitfield(behaviorBitfield, protocol.BITFIELD_DOESACK):
behaviorBitfield, protocol.BITFIELD_DOESACK):
newStatus = 'msgsentnoackexpected' newStatus = 'msgsentnoackexpected'
else: else:
newStatus = 'msgsent' newStatus = 'msgsent'
@ -1270,6 +1265,7 @@ class singleWorker(threading.Thread, StoppableThread):
call([apiNotifyPath, "newMessage"]) call([apiNotifyPath, "newMessage"])
def requestPubKey(self, toAddress): def requestPubKey(self, toAddress):
"""Send a getpubkey object"""
toStatus, addressVersionNumber, streamNumber, ripe = decodeAddress( toStatus, addressVersionNumber, streamNumber, ripe = decodeAddress(
toAddress) toAddress)
if toStatus != 'success': if toStatus != 'success':
@ -1286,7 +1282,7 @@ class singleWorker(threading.Thread, StoppableThread):
''' LIMIT 1''', ''' LIMIT 1''',
toAddress toAddress
) )
if len(queryReturn) == 0: if not queryReturn:
logger.critical( logger.critical(
'BUG: Why are we requesting the pubkey for %s' 'BUG: Why are we requesting the pubkey for %s'
' if there are no messages in the sent folder' ' if there are no messages in the sent folder'
@ -1389,16 +1385,14 @@ class singleWorker(threading.Thread, StoppableThread):
).arg(l10n.formatTimestamp())) ).arg(l10n.formatTimestamp()))
)) ))
def generateFullAckMessage(self, ackdata, toStreamNumber, TTL): 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 might be perfectly fine to just use the same TTL for the ackdata that we use for the message. But I would
# it be more difficult for attackers to associate ackData with rather it be more difficult for attackers to associate ackData with the associated msg object. However, users
# the associated msg object. However, users would want the TTL would want the TTL of the acknowledgement to be about the same as they set for the message itself. So let's set
# of the acknowledgement to be about the same as they set the TTL of the acknowledgement to be in one of three 'buckets': 1 hour, 7 days, or 28 days, whichever is
# for the message itself. So let's set the TTL of the relatively close to what the user specified.
# 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 if TTL < 24 * 60 * 60: # 1 day
TTL = 24 * 60 * 60 # 1 day TTL = 24 * 60 * 60 # 1 day
elif TTL < 7 * 24 * 60 * 60: # 1 week elif TTL < 7 * 24 * 60 * 60: # 1 week