diff --git a/.readthedocs.yml b/.readthedocs.yml
new file mode 100644
index 00000000..474ae9ab
--- /dev/null
+++ b/.readthedocs.yml
@@ -0,0 +1,9 @@
+version: 2
+
+python:
+ version: 2.7
+ install:
+ - requirements: docs/requirements.txt
+ - method: setuptools
+ path: .
+ system_packages: true
diff --git a/src/addresses.py b/src/addresses.py
index b83f3f6e..bb0c9ec5 100644
--- a/src/addresses.py
+++ b/src/addresses.py
@@ -1,7 +1,5 @@
"""
-src/addresses.py
-================
-
+Operations with addresses
"""
# pylint: disable=redefined-outer-name,inconsistent-return-statements
@@ -18,8 +16,9 @@ ALPHABET = "123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz"
def encodeBase58(num, alphabet=ALPHABET):
"""Encode a number in Base X
- `num`: The number to encode
- `alphabet`: The alphabet to use for encoding
+ Args:
+ num: The number to encode
+ alphabet: The alphabet to use for encoding
"""
if num == 0:
return alphabet[0]
@@ -27,7 +26,6 @@ def encodeBase58(num, alphabet=ALPHABET):
base = len(alphabet)
while num:
rem = num % base
- # print 'num is:', num
num = num // base
arr.append(alphabet[rem])
arr.reverse()
@@ -37,9 +35,9 @@ def encodeBase58(num, alphabet=ALPHABET):
def decodeBase58(string, alphabet=ALPHABET):
"""Decode a Base X encoded string into the number
- Arguments:
- - `string`: The encoded string
- - `alphabet`: The alphabet to use for encoding
+ Args:
+ string: The encoded string
+ alphabet: The alphabet to use for encoding
"""
base = len(alphabet)
num = 0
diff --git a/src/bitmessageqt/__init__.py b/src/bitmessageqt/__init__.py
index 440d36b2..c7fa6390 100644
--- a/src/bitmessageqt/__init__.py
+++ b/src/bitmessageqt/__init__.py
@@ -11,6 +11,7 @@ import sys
import textwrap
import threading
import time
+import base64
from datetime import datetime, timedelta
from sqlite3 import register_adapter
@@ -161,6 +162,8 @@ class MyForm(settingsmixin.SMainWindow):
"clicked()"), self.click_pushButtonTTL)
QtCore.QObject.connect(self.ui.pushButtonClear, QtCore.SIGNAL(
"clicked()"), self.click_pushButtonClear)
+ QtCore.QObject.connect(self.ui.pushButtonAttach, QtCore.SIGNAL(
+ "clicked()"), self.click_pushButtonAttach)
QtCore.QObject.connect(self.ui.pushButtonSend, QtCore.SIGNAL(
"clicked()"), self.click_pushButtonSend)
QtCore.QObject.connect(self.ui.pushButtonFetchNamecoinID, QtCore.SIGNAL(
@@ -1951,6 +1954,23 @@ class MyForm(settingsmixin.SMainWindow):
self.ui.textEditMessage.reset()
self.ui.comboBoxSendFrom.setCurrentIndex(0)
+ def click_pushButtonAttach(self):
+ """Launch a file picker and append to the current message the base64-encoded contents of the chosen file."""
+ filename = QtGui.QFileDialog.getOpenFileName(self, "Attach File")
+ if filename:
+ f = open(filename, 'rb')
+ data = f.read()
+ f.close()
+ data_b64 = base64.b64encode(data)
+ html_data = '' \
+ + os.path.basename(unicode(filename)) + ''
+ if self.ui.tabWidgetSend.currentIndex() == self.ui.tabWidgetSend.indexOf(self.ui.sendDirect):
+ # send direct message
+ self.ui.textEditMessage.insertPlainText(html_data)
+ else:
+ # send broadcast message
+ self.ui.textEditMessageBroadcast.insertPlainText(html_data)
+
def click_pushButtonSend(self):
encoding = 3 if QtGui.QApplication.queryKeyboardModifiers() & QtCore.Qt.ShiftModifier else 2
diff --git a/src/bitmessageqt/bitmessageui.py b/src/bitmessageqt/bitmessageui.py
index 30d054d0..ca103dd5 100644
--- a/src/bitmessageqt/bitmessageui.py
+++ b/src/bitmessageqt/bitmessageui.py
@@ -340,6 +340,9 @@ class Ui_MainWindow(object):
self.pushButtonClear = QtGui.QPushButton(self.send)
self.pushButtonClear.setObjectName(_fromUtf8("pushButtonClear"))
self.horizontalLayout_5.addWidget(self.pushButtonClear, 0, QtCore.Qt.AlignRight)
+ self.pushButtonAttach = QtGui.QPushButton(self.send)
+ self.pushButtonAttach.setObjectName(_fromUtf8("pushButtonAttach"))
+ self.horizontalLayout_5.addWidget(self.pushButtonAttach, 0, QtCore.Qt.AlignRight)
self.pushButtonSend = QtGui.QPushButton(self.send)
self.pushButtonSend.setObjectName(_fromUtf8("pushButtonSend"))
self.horizontalLayout_5.addWidget(self.pushButtonSend, 0, QtCore.Qt.AlignRight)
@@ -713,6 +716,7 @@ class Ui_MainWindow(object):
pass
self.labelHumanFriendlyTTLDescription.setText(_translate("MainWindow", "%n hour(s)", None, QtCore.QCoreApplication.CodecForTr, hours))
self.pushButtonClear.setText(_translate("MainWindow", "Clear", None))
+ self.pushButtonAttach.setText(_translate("MainWindow", "Attach File", None))
self.pushButtonSend.setText(_translate("MainWindow", "Send", None))
self.tabWidget.setTabText(self.tabWidget.indexOf(self.send), _translate("MainWindow", "Send", None))
self.treeWidgetSubscriptions.headerItem().setText(0, _translate("MainWindow", "Subscriptions", None))
diff --git a/src/bitmessageqt/bitmessageui.ui b/src/bitmessageqt/bitmessageui.ui
index fef40be6..b8b92b68 100644
--- a/src/bitmessageqt/bitmessageui.ui
+++ b/src/bitmessageqt/bitmessageui.ui
@@ -594,6 +594,19 @@ p, li { white-space: pre-wrap; }
+ -
+
+
+
+ 16777215
+ 16777215
+
+
+
+ Attach File
+
+
+
diff --git a/src/bitmessageqt/messageview.py b/src/bitmessageqt/messageview.py
index 45f3a79a..7a6432b0 100644
--- a/src/bitmessageqt/messageview.py
+++ b/src/bitmessageqt/messageview.py
@@ -5,6 +5,8 @@ src/bitmessageqt/messageview.py
"""
from PyQt4 import QtCore, QtGui
+import re
+import base64
from safehtmlparser import SafeHTMLParser
@@ -64,6 +66,9 @@ class MessageView(QtGui.QTextBrowser):
def confirmURL(self, link):
"""Show a dialog requesting URL opening confirmation"""
+ link_str = link.toString()
+ datablob_re = r'^data:.*/.*;base64,.*'
+ datablob_match = re.match(datablob_re, link_str)
if link.scheme() == "mailto":
window = QtGui.QApplication.activeWindow()
window.ui.lineEditTo.setText(link.path())
@@ -80,19 +85,29 @@ class MessageView(QtGui.QTextBrowser):
)
window.ui.textEditMessage.setFocus()
return
- reply = QtGui.QMessageBox.warning(
- self,
- QtGui.QApplication.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:
- QtGui.QDesktopServices.openUrl(link)
+ if datablob_match:
+ name = QtGui.QFileDialog.getSaveFileName(self, 'Save File')
+ if name:
+ f = open(name, 'wb')
+ data_begin_pos = re.finditer(";base64,", link_str).next()
+ data_b64 = link_str[data_begin_pos.span()[1]:]
+ data = base64.b64decode(data_b64)
+ f.write(data)
+ f.close()
+ else:
+ reply = QtGui.QMessageBox.warning(
+ self,
+ QtGui.QApplication.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:
+ QtGui.QDesktopServices.openUrl(link)
def loadResource(self, restype, name):
"""
diff --git a/src/highlevelcrypto.py b/src/highlevelcrypto.py
index 3d894ae8..f392fe4a 100644
--- a/src/highlevelcrypto.py
+++ b/src/highlevelcrypto.py
@@ -1,6 +1,10 @@
"""
-src/highlevelcrypto.py
-======================
+High level cryptographic functions based on `.pyelliptic` OpenSSL bindings.
+
+.. note::
+ Upstream pyelliptic was upgraded from SHA1 to SHA256 for signing.
+ We must upgrade PyBitmessage gracefully.
+ `More discussion. `_
"""
from binascii import hexlify
@@ -12,12 +16,13 @@ from pyelliptic import arithmetic as a
def makeCryptor(privkey):
- """Return a private pyelliptic.ECC() instance"""
+ """Return a private `.pyelliptic.ECC` instance"""
private_key = a.changebase(privkey, 16, 256, minlen=32)
public_key = pointMult(private_key)
privkey_bin = '\x02\xca\x00\x20' + private_key
pubkey_bin = '\x02\xca\x00\x20' + public_key[1:-32] + '\x00\x20' + public_key[-32:]
- cryptor = pyelliptic.ECC(curve='secp256k1', privkey=privkey_bin, pubkey=pubkey_bin)
+ cryptor = pyelliptic.ECC(
+ curve='secp256k1', privkey=privkey_bin, pubkey=pubkey_bin)
return cryptor
@@ -29,7 +34,7 @@ def hexToPubkey(pubkey):
def makePubCryptor(pubkey):
- """Return a public pyelliptic.ECC() instance"""
+ """Return a public `.pyelliptic.ECC` instance"""
pubkey_bin = hexToPubkey(pubkey)
return pyelliptic.ECC(curve='secp256k1', pubkey=pubkey_bin)
@@ -43,7 +48,8 @@ def privToPub(privkey):
def encrypt(msg, hexPubkey):
"""Encrypts message with hex public key"""
- return pyelliptic.ECC(curve='secp256k1').encrypt(msg, hexToPubkey(hexPubkey))
+ return pyelliptic.ECC(curve='secp256k1').encrypt(
+ msg, hexToPubkey(hexPubkey))
def decrypt(msg, hexPrivkey):
@@ -52,36 +58,38 @@ def decrypt(msg, hexPrivkey):
def decryptFast(msg, cryptor):
- """Decrypts message with an existing pyelliptic.ECC.ECC object"""
+ """Decrypts message with an existing `.pyelliptic.ECC` object"""
return cryptor.decrypt(msg)
def sign(msg, hexPrivkey):
- """Signs with hex private key"""
- # pyelliptic is upgrading from SHA1 to SHA256 for signing. We must
- # upgrade PyBitmessage gracefully.
- # https://github.com/yann2192/pyelliptic/pull/33
- # More discussion: https://github.com/yann2192/pyelliptic/issues/32
- digestAlg = BMConfigParser().safeGet('bitmessagesettings', 'digestalg', 'sha1')
+ """
+ Signs with hex private key using SHA1 or SHA256 depending on
+ "digestalg" setting
+ """
+ digestAlg = BMConfigParser().safeGet(
+ 'bitmessagesettings', 'digestalg', 'sha1')
if digestAlg == "sha1":
# SHA1, this will eventually be deprecated
- return makeCryptor(hexPrivkey).sign(msg, digest_alg=OpenSSL.digest_ecdsa_sha1)
+ return makeCryptor(hexPrivkey).sign(
+ msg, digest_alg=OpenSSL.digest_ecdsa_sha1)
elif digestAlg == "sha256":
# SHA256. Eventually this will become the default
return makeCryptor(hexPrivkey).sign(msg, digest_alg=OpenSSL.EVP_sha256)
else:
- raise ValueError("Unknown digest algorithm %s" % (digestAlg))
+ raise ValueError("Unknown digest algorithm %s" % digestAlg)
def verify(msg, sig, hexPubkey):
- """Verifies with hex public key"""
+ """Verifies with hex public key using SHA1 or SHA256"""
# As mentioned above, we must upgrade gracefully to use SHA256. So
# let us check the signature using both SHA1 and SHA256 and if one
# of them passes then we will be satisfied. Eventually this can
# be simplified and we'll only check with SHA256.
try:
# old SHA1 algorithm.
- sigVerifyPassed = makePubCryptor(hexPubkey).verify(sig, msg, digest_alg=OpenSSL.digest_ecdsa_sha1)
+ sigVerifyPassed = makePubCryptor(hexPubkey).verify(
+ sig, msg, digest_alg=OpenSSL.digest_ecdsa_sha1)
except:
sigVerifyPassed = False
if sigVerifyPassed:
@@ -89,7 +97,8 @@ def verify(msg, sig, hexPubkey):
return True
# The signature check using SHA1 failed. Let us try it with SHA256.
try:
- return makePubCryptor(hexPubkey).verify(sig, msg, digest_alg=OpenSSL.EVP_sha256)
+ return makePubCryptor(hexPubkey).verify(
+ sig, msg, digest_alg=OpenSSL.EVP_sha256)
except:
return False
@@ -106,7 +115,8 @@ def pointMult(secret):
"""
while True:
try:
- k = OpenSSL.EC_KEY_new_by_curve_name(OpenSSL.get_curve('secp256k1'))
+ k = OpenSSL.EC_KEY_new_by_curve_name(
+ OpenSSL.get_curve('secp256k1'))
priv_key = OpenSSL.BN_bin2bn(secret, 32, None)
group = OpenSSL.EC_KEY_get0_group(k)
pub_key = OpenSSL.EC_POINT_new(group)
diff --git a/src/protocol.py b/src/protocol.py
index ec8fc9dd..cdd50dce 100644
--- a/src/protocol.py
+++ b/src/protocol.py
@@ -1,7 +1,8 @@
-# pylint: disable=too-many-boolean-expressions,too-many-return-statements,too-many-locals,too-many-statements
"""
Low-level protocol-related functions.
"""
+# pylint: disable=too-many-boolean-expressions,too-many-return-statements
+# pylint: disable=too-many-locals,too-many-statements
import base64
import hashlib
@@ -9,7 +10,6 @@ import random
import socket
import sys
import time
-import traceback
from binascii import hexlify
from struct import pack, unpack, Struct
@@ -24,10 +24,18 @@ from fallback import RIPEMD160Hash
from helper_sql import sqlExecute
from version import softwareVersion
-
# Service flags
+#: This is a normal network node
NODE_NETWORK = 1
+#: This node supports SSL/TLS in the current connect (python < 2.7.9
+#: only supports an SSL client, so in that case it would only have this
+#: on when the connection is a client).
NODE_SSL = 2
+# (Proposal) This node may do PoW on behalf of some its peers
+# (PoW offloading/delegating), but it doesn't have to. Clients may have
+# to meet additional requirements (e.g. TLS authentication)
+# NODE_POW = 4
+#: Node supports dandelion
NODE_DANDELION = 8
# Bitfield flags
@@ -89,7 +97,8 @@ def isBitSetWithinBitfield(fourByteString, n):
def encodeHost(host):
"""Encode a given host to be used in low-level socket operations"""
if host.find('.onion') > -1:
- return '\xfd\x87\xd8\x7e\xeb\x43' + base64.b32decode(host.split(".")[0], True)
+ return '\xfd\x87\xd8\x7e\xeb\x43' + base64.b32decode(
+ host.split(".")[0], True)
elif host.find(':') == -1:
return '\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xFF\xFF' + \
socket.inet_aton(host)
@@ -134,7 +143,10 @@ def network_group(host):
def checkIPAddress(host, private=False):
- """Returns hostStandardFormat if it is a valid IP address, otherwise returns False"""
+ """
+ Returns hostStandardFormat if it is a valid IP address,
+ otherwise returns False
+ """
if host[0:12] == '\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xFF\xFF':
hostStandardFormat = socket.inet_ntop(socket.AF_INET, host[12:])
return checkIPv4Address(host[12:], hostStandardFormat, private)
@@ -150,35 +162,46 @@ def checkIPAddress(host, private=False):
except ValueError:
return False
if hostStandardFormat == "":
- # This can happen on Windows systems which are not 64-bit compatible
- # so let us drop the IPv6 address.
+ # This can happen on Windows systems which are
+ # not 64-bit compatible so let us drop the IPv6 address.
return False
return checkIPv6Address(host, hostStandardFormat, private)
def checkIPv4Address(host, hostStandardFormat, private=False):
- """Returns hostStandardFormat if it is an IPv4 address, otherwise returns False"""
+ """
+ Returns hostStandardFormat if it is an IPv4 address,
+ otherwise returns False
+ """
if host[0] == '\x7F': # 127/8
if not private:
- logger.debug('Ignoring IP address in loopback range: %s', hostStandardFormat)
+ logger.debug(
+ 'Ignoring IP address in loopback range: %s',
+ hostStandardFormat)
return hostStandardFormat if private else False
if host[0] == '\x0A': # 10/8
if not private:
- logger.debug('Ignoring IP address in private range: %s', hostStandardFormat)
+ logger.debug(
+ 'Ignoring IP address in private range: %s', hostStandardFormat)
return hostStandardFormat if private else False
if host[0:2] == '\xC0\xA8': # 192.168/16
if not private:
- logger.debug('Ignoring IP address in private range: %s', hostStandardFormat)
+ logger.debug(
+ 'Ignoring IP address in private range: %s', hostStandardFormat)
return hostStandardFormat if private else False
if host[0:2] >= '\xAC\x10' and host[0:2] < '\xAC\x20': # 172.16/12
if not private:
- logger.debug('Ignoring IP address in private range: %s', hostStandardFormat)
+ logger.debug(
+ 'Ignoring IP address in private range: %s', hostStandardFormat)
return hostStandardFormat if private else False
return False if private else hostStandardFormat
def checkIPv6Address(host, hostStandardFormat, private=False):
- """Returns hostStandardFormat if it is an IPv6 address, otherwise returns False"""
+ """
+ Returns hostStandardFormat if it is an IPv6 address,
+ otherwise returns False
+ """
if host == ('\x00' * 15) + '\x01':
if not private:
logger.debug('Ignoring loopback address: %s', hostStandardFormat)
@@ -189,7 +212,8 @@ def checkIPv6Address(host, hostStandardFormat, private=False):
return hostStandardFormat if private else False
if (ord(host[0]) & 0xfe) == 0xfc:
if not private:
- logger.debug('Ignoring unique local address: %s', hostStandardFormat)
+ logger.debug(
+ 'Ignoring unique local address: %s', hostStandardFormat)
return hostStandardFormat if private else False
return False if private else hostStandardFormat
@@ -210,31 +234,29 @@ def haveSSL(server=False):
def checkSocksIP(host):
"""Predicate to check if we're using a SOCKS proxy"""
+ sockshostname = BMConfigParser().safeGet(
+ 'bitmessagesettings', 'sockshostname')
try:
- if state.socksIP is None or not state.socksIP:
- state.socksIP = socket.gethostbyname(BMConfigParser().get("bitmessagesettings", "sockshostname"))
- # uninitialised
- except NameError:
- state.socksIP = socket.gethostbyname(BMConfigParser().get("bitmessagesettings", "sockshostname"))
- # resolving failure
- except socket.gaierror:
- state.socksIP = BMConfigParser().get("bitmessagesettings", "sockshostname")
+ if not state.socksIP:
+ state.socksIP = socket.gethostbyname(sockshostname)
+ except NameError: # uninitialised
+ state.socksIP = socket.gethostbyname(sockshostname)
+ except (TypeError, socket.gaierror): # None, resolving failure
+ state.socksIP = sockshostname
return state.socksIP == host
-def isProofOfWorkSufficient(data,
- nonceTrialsPerByte=0,
- payloadLengthExtraBytes=0,
- recvTime=0):
+def isProofOfWorkSufficient(
+ data, nonceTrialsPerByte=0, payloadLengthExtraBytes=0, recvTime=0):
"""
- Validate an object's Proof of Work using method described in:
- https://bitmessage.org/wiki/Proof_of_work
+ Validate an object's Proof of Work using method described
+ `here `_
Arguments:
- int nonceTrialsPerByte (default: from default.py)
- int payloadLengthExtraBytes (default: from default.py)
+ int nonceTrialsPerByte (default: from `.defaults`)
+ int payloadLengthExtraBytes (default: from `.defaults`)
float recvTime (optional) UNIX epoch time when object was
- received from the network (default: current system time)
+ received from the network (default: current system time)
Returns:
True if PoW valid and sufficient, False in all other cases
"""
@@ -246,18 +268,20 @@ def isProofOfWorkSufficient(data,
TTL = endOfLifeTime - (int(recvTime) if recvTime else int(time.time()))
if TTL < 300:
TTL = 300
- POW, = unpack('>Q', hashlib.sha512(hashlib.sha512(data[
- :8] + hashlib.sha512(data[8:]).digest()).digest()).digest()[0:8])
- return POW <= 2 ** 64 / (nonceTrialsPerByte *
- (len(data) + payloadLengthExtraBytes +
- ((TTL * (len(data) + payloadLengthExtraBytes)) / (2 ** 16))))
+ POW, = unpack('>Q', hashlib.sha512(hashlib.sha512(
+ data[:8] + hashlib.sha512(data[8:]).digest()
+ ).digest()).digest()[0:8])
+ return POW <= 2 ** 64 / (
+ nonceTrialsPerByte * (
+ len(data) + payloadLengthExtraBytes +
+ ((TTL * (len(data) + payloadLengthExtraBytes)) / (2 ** 16))))
# Packet creation
def CreatePacket(command, payload=''):
- """Construct and return a number of bytes from a payload"""
+ """Construct and return a packet"""
payload_length = len(payload)
checksum = hashlib.sha512(payload).digest()[0:4]
@@ -267,8 +291,13 @@ def CreatePacket(command, payload=''):
return bytes(b)
-def assembleVersionMessage(remoteHost, remotePort, participatingStreams, server=False, nodeid=None):
- """Construct the payload of a version message, return the resultng bytes of running CreatePacket() on it"""
+def assembleVersionMessage(
+ remoteHost, remotePort, participatingStreams, server=False, nodeid=None
+):
+ """
+ Construct the payload of a version message,
+ return the resulting bytes of running `CreatePacket` on it
+ """
payload = ''
payload += pack('>L', 3) # protocol version.
# bitflags of the services I offer.
@@ -280,9 +309,10 @@ def assembleVersionMessage(remoteHost, remotePort, participatingStreams, server=
)
payload += pack('>q', int(time.time()))
- payload += pack(
- '>q', 1) # boolservices of remote connection; ignored by the remote host.
- if checkSocksIP(remoteHost) and server: # prevent leaking of tor outbound IP
+ # boolservices of remote connection; ignored by the remote host.
+ payload += pack('>q', 1)
+ if checkSocksIP(remoteHost) and server:
+ # prevent leaking of tor outbound IP
payload += encodeHost('127.0.0.1')
payload += pack('>H', 8444)
else:
@@ -301,21 +331,25 @@ def assembleVersionMessage(remoteHost, remotePort, participatingStreams, server=
(NODE_SSL if haveSSL(server) else 0) |
(NODE_DANDELION if state.dandelion else 0)
)
- # = 127.0.0.1. This will be ignored by the remote host. The actual remote connected IP will be used.
- payload += '\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xFF\xFF' + pack('>L', 2130706433)
+ # = 127.0.0.1. This will be ignored by the remote host.
+ # The actual remote connected IP will be used.
+ payload += '\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xFF\xFF' + pack(
+ '>L', 2130706433)
# we have a separate extPort and incoming over clearnet
# or outgoing through clearnet
extport = BMConfigParser().safeGetInt('bitmessagesettings', 'extport')
if (
extport and ((server and not checkSocksIP(remoteHost)) or (
- BMConfigParser().get('bitmessagesettings', 'socksproxytype') ==
- 'none' and not server))
+ BMConfigParser().get('bitmessagesettings', 'socksproxytype')
+ == 'none' and not server))
):
payload += pack('>H', extport)
elif checkSocksIP(remoteHost) and server: # incoming connection over Tor
- payload += pack('>H', BMConfigParser().getint('bitmessagesettings', 'onionport'))
+ payload += pack(
+ '>H', BMConfigParser().getint('bitmessagesettings', 'onionport'))
else: # no extport and not incoming over Tor
- payload += pack('>H', BMConfigParser().getint('bitmessagesettings', 'port'))
+ payload += pack(
+ '>H', BMConfigParser().getint('bitmessagesettings', 'port'))
if nodeid is not None:
payload += nodeid[0:8]
@@ -339,7 +373,10 @@ def assembleVersionMessage(remoteHost, remotePort, participatingStreams, server=
def assembleErrorMessage(fatal=0, banTime=0, inventoryVector='', errorText=''):
- """Construct the payload of an error message, return the resultng bytes of running CreatePacket() on it"""
+ """
+ Construct the payload of an error message,
+ return the resulting bytes of running `CreatePacket` on it
+ """
payload = encodeVarint(fatal)
payload += encodeVarint(banTime)
payload += encodeVarint(len(inventoryVector))
@@ -476,7 +513,7 @@ def decryptAndCheckPubkeyPayload(data, address):
except Exception:
logger.critical(
'Pubkey decryption was UNsuccessful because of'
- ' an unhandled exception! This is definitely a bug! \n%s',
- traceback.format_exc()
+ ' an unhandled exception! This is definitely a bug!',
+ exc_info=True
)
return 'failed'
diff --git a/src/shared.py b/src/shared.py
index 1a2add28..90cea89d 100644
--- a/src/shared.py
+++ b/src/shared.py
@@ -80,7 +80,9 @@ def isAddressInMySubscriptionsList(address):
def isAddressInMyAddressBookSubscriptionsListOrWhitelist(address):
- """Am I subscribed to this address, is it in my addressbook or whitelist?"""
+ """
+ Am I subscribed to this address, is it in my addressbook or whitelist?
+ """
if isAddressInMyAddressBook(address):
return True
@@ -100,8 +102,12 @@ def isAddressInMyAddressBookSubscriptionsListOrWhitelist(address):
return False
-def decodeWalletImportFormat(WIFstring): # pylint: disable=inconsistent-return-statements
- """Convert private key from base58 that's used in the config file to 8-bit binary string"""
+def decodeWalletImportFormat(WIFstring):
+ # pylint: disable=inconsistent-return-statements
+ """
+ Convert private key from base58 that's used in the config file to
+ 8-bit binary string
+ """
fullString = arithmetic.changebase(WIFstring, 58, 256)
privkey = fullString[:-4]
if fullString[-4:] != \
@@ -126,14 +132,15 @@ def decodeWalletImportFormat(WIFstring): # pylint: disable=inconsistent-retur
def reloadMyAddressHashes():
- """Reinitialise runtime data (e.g. encryption objects, address hashes) from the config file"""
+ """Reload keys for user's addresses from the config file"""
logger.debug('reloading keys from keys.dat file')
myECCryptorObjects.clear()
myAddressesByHash.clear()
myAddressesByTag.clear()
# myPrivateKeys.clear()
- keyfileSecure = checkSensitiveFilePermissions(state.appdata + 'keys.dat')
+ keyfileSecure = checkSensitiveFilePermissions(os.path.join(
+ state.appdata, 'keys.dat'))
hasEnabledKeys = False
for addressInKeysFile in BMConfigParser().addresses():
isEnabled = BMConfigParser().getboolean(addressInKeysFile, 'enabled')
@@ -162,11 +169,15 @@ def reloadMyAddressHashes():
)
if not keyfileSecure:
- fixSensitiveFilePermissions(state.appdata + 'keys.dat', hasEnabledKeys)
+ fixSensitiveFilePermissions(os.path.join(
+ state.appdata, 'keys.dat'), hasEnabledKeys)
def reloadBroadcastSendersForWhichImWatching():
- """Reinitialise runtime data for the broadcasts I'm subscribed to from the config file"""
+ """
+ Reinitialize runtime data for the broadcasts I'm subscribed to
+ from the config file
+ """
broadcastSendersForWhichImWatching.clear()
MyECSubscriptionCryptorObjects.clear()
queryreturn = sqlQuery('SELECT address FROM subscriptions where enabled=1')
diff --git a/src/shutdown.py b/src/shutdown.py
index c81a519a..dbc2af04 100644
--- a/src/shutdown.py
+++ b/src/shutdown.py
@@ -16,7 +16,9 @@ from queues import (
def doCleanShutdown():
- """Used to tell proof of work worker threads and the objectProcessorThread to exit."""
+ """
+ Used to tell all the treads to finish work and exit.
+ """
state.shutdown = 1
objectProcessorQueue.put(('checkShutdownVariable', 'no data'))
@@ -52,9 +54,11 @@ def doCleanShutdown():
time.sleep(.25)
for thread in threading.enumerate():
- if (thread is not threading.currentThread() and
- isinstance(thread, StoppableThread) and
- thread.name != 'SQL'):
+ if (
+ thread is not threading.currentThread()
+ and isinstance(thread, StoppableThread)
+ and thread.name != 'SQL'
+ ):
logger.debug("Waiting for thread %s", thread.name)
thread.join()
diff --git a/src/state.py b/src/state.py
index f5526029..58e1106a 100644
--- a/src/state.py
+++ b/src/state.py
@@ -16,8 +16,8 @@ appdata = ''
shutdown = 0
"""
- Set to 1 by the doCleanShutdown function.
- Used to tell the proof of work worker threads to exit.
+Set to 1 by the `.shutdown.doCleanShutdown` function.
+Used to tell the threads to exit.
"""
# Component control flags - set on startup, do not change during runtime
@@ -25,7 +25,7 @@ shutdown = 0
enableNetwork = True
"""enable network threads"""
enableObjProc = True
-"""enable object processing threads"""
+"""enable object processing thread"""
enableAPI = True
"""enable API (if configured)"""
enableGUI = True
@@ -35,7 +35,7 @@ enableSTDIO = False
curses = False
sqlReady = False
-"""set to true by sqlTread when ready for processing"""
+"""set to true by `.threads.sqlThread` when ready for processing"""
maximumNumberOfHalfOpenConnections = 0