Attachments a (#13) sq n mg

* Formatted protocol and its docstrings

* Fixed some docstrings in shared and state

* Fixed google style docstrings in addresses

* More docstrings and formatting fixes in highlevelcrypto and shutdown

* .readthedocs.yml

* When clicking a link in a message viewed in HTML mode, if the link represents a data blob, launch a "Save File" dialog and write the file directly, rather than opening the link in an external browser.

* Add "Attach File" button to message composition.

* pylint fixes.
This commit is contained in:
bug Lady 2019-11-29 14:36:36 +00:00 committed by GitHub
parent bb40d970cd
commit c4e5385554
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
11 changed files with 228 additions and 107 deletions

9
.readthedocs.yml Normal file
View File

@ -0,0 +1,9 @@
version: 2
python:
version: 2.7
install:
- requirements: docs/requirements.txt
- method: setuptools
path: .
system_packages: true

View File

@ -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

View File

@ -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 = '<a href="data:application/octet-stream;base64,' + data_b64 + '">' \
+ os.path.basename(unicode(filename)) + '</a>'
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

View File

@ -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))

View File

@ -594,6 +594,19 @@ p, li { white-space: pre-wrap; }
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="pushButtonAttach">
<property name="maximumSize">
<size>
<width>16777215</width>
<height>16777215</height>
</size>
</property>
<property name="text">
<string>Attach File</string>
</property>
</widget>
</item>
</layout>
</item>
</layout>

View File

@ -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):
"""

View File

@ -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. <https://github.com/yann2192/pyelliptic/issues/32>`_
"""
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)

View File

@ -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 <https://bitmessage.org/wiki/Proof_of_work>`_
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'

View File

@ -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')

View File

@ -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()

View File

@ -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