diff --git a/setup.cfg b/setup.cfg
index 32abcdc7..4e185775 100644
--- a/setup.cfg
+++ b/setup.cfg
@@ -1,9 +1,11 @@
[pycodestyle]
max-line-length = 119
+ignore = E722,E402
[flake8]
max-line-length = 119
-ignore = E722
+ignore = E722,E402
+# E402: pylint is preferred for wrong-import-position
# pylint
[MESSAGES CONTROL]
diff --git a/src/api.py b/src/api.py
index a32519e4..6709162b 100644
--- a/src/api.py
+++ b/src/api.py
@@ -1,19 +1,24 @@
+# pylint: disable=too-many-locals,too-many-lines,no-self-use,too-many-public-methods,too-many-branches
+# pylint: disable=too-many-statements
+"""
# Copyright (c) 2012-2016 Jonathan Warren
# Copyright (c) 2012-2018 The Bitmessage developers
-"""
This is not what you run to run the Bitmessage API. Instead, enable the API
( https://bitmessage.org/wiki/API ) and optionally enable daemon mode
( https://bitmessage.org/wiki/Daemon ) then run bitmessagemain.py.
"""
+from __future__ import absolute_import
+
import base64
import hashlib
import json
+from struct import pack
import time
from binascii import hexlify, unhexlify
+
from SimpleXMLRPCServer import SimpleXMLRPCRequestHandler, SimpleXMLRPCServer
-from struct import pack
import shared
from addresses import (
@@ -43,6 +48,8 @@ str_chan = '[chan]'
class APIError(Exception):
+ """APIError exception class"""
+
def __init__(self, error_number, error_message):
super(APIError, self).__init__()
self.error_number = error_number
@@ -53,26 +60,34 @@ class APIError(Exception):
class StoppableXMLRPCServer(SimpleXMLRPCServer):
+ """A SimpleXMLRPCServer that honours state.shutdown"""
allow_reuse_address = True
def serve_forever(self):
+ """Start the SimpleXMLRPCServer"""
+ # pylint: disable=arguments-differ
while state.shutdown == 0:
self.handle_request()
-# This is one of several classes that constitute the API
-# This class was written by Vaibhav Bhatia.
-# Modified by Jonathan Warren (Atheros).
-# http://code.activestate.com/recipes/501148-xmlrpc-serverclient-which-does-cookie-handling-and/
class MySimpleXMLRPCRequestHandler(SimpleXMLRPCRequestHandler):
+ """
+ This is one of several classes that constitute the API
+
+ This class was written by Vaibhav Bhatia. Modified by Jonathan Warren (Atheros).
+ http://code.activestate.com/recipes/501148-xmlrpc-serverclient-which-does-cookie-handling-and/
+ """
def do_POST(self):
- # Handles the HTTP POST request.
- # Attempts to interpret all HTTP POST requests as XML-RPC calls,
- # which are forwarded to the server's _dispatch method for handling.
+ """
+ Handles the HTTP POST request.
- # Note: this method is the same as in SimpleXMLRPCRequestHandler,
- # just hacked to handle cookies
+ Attempts to interpret all HTTP POST requests as XML-RPC calls,
+ which are forwarded to the server's _dispatch method for handling.
+
+ Note: this method is the same as in SimpleXMLRPCRequestHandler,
+ just hacked to handle cookies
+ """
# Check that the path is legal
if not self.is_rpc_path_valid():
@@ -98,7 +113,7 @@ class MySimpleXMLRPCRequestHandler(SimpleXMLRPCRequestHandler):
# SimpleXMLRPCDispatcher. To maintain backwards compatibility,
# check to see if a subclass implements _dispatch and dispatch
# using that method if present.
- response = self.server._marshaled_dispatch(
+ response = self.server._marshaled_dispatch( # pylint: disable=protected-access
data, getattr(self, '_dispatch', None)
)
except: # This should only happen if the module is buggy
@@ -125,22 +140,21 @@ class MySimpleXMLRPCRequestHandler(SimpleXMLRPCRequestHandler):
self.connection.shutdown(1)
def APIAuthenticateClient(self):
+ """Predicate to check for valid API credentials in the request header"""
+
if 'Authorization' in self.headers:
# handle Basic authentication
- enctype, encstr = self.headers.get('Authorization').split()
+ _, encstr = self.headers.get('Authorization').split()
emailid, password = encstr.decode('base64').split(':')
return (
- emailid ==
- BMConfigParser().get('bitmessagesettings', 'apiusername')
- and password ==
- BMConfigParser().get('bitmessagesettings', 'apipassword')
+ emailid == BMConfigParser().get('bitmessagesettings', 'apiusername') and
+ password == BMConfigParser().get('bitmessagesettings', 'apipassword')
)
else:
logger.warning(
'Authentication failed because header lacks'
' Authentication field')
time.sleep(2)
- return False
return False
@@ -155,6 +169,7 @@ class MySimpleXMLRPCRequestHandler(SimpleXMLRPCRequestHandler):
22, "Decode error - %s. Had trouble while decoding string: %r"
% (e, text)
)
+ return None
def _verifyAddress(self, address):
status, addressVersionNumber, streamNumber, ripe = \
@@ -170,15 +185,10 @@ class MySimpleXMLRPCRequestHandler(SimpleXMLRPCRequestHandler):
if status == 'invalidcharacters':
raise APIError(9, 'Invalid characters in address: ' + address)
if status == 'versiontoohigh':
- raise APIError(
- 10,
- 'Address version number too high (or zero) in address: '
- + address
- )
+ raise APIError(10, 'Address version number too high (or zero) in address: ' + address)
if status == 'varintmalformed':
raise APIError(26, 'Malformed varint in address: ' + address)
- raise APIError(
- 7, 'Could not decode address: %s : %s' % (address, status))
+ raise APIError(7, 'Could not decode address: %s : %s' % (address, status))
if addressVersionNumber < 2 or addressVersionNumber > 4:
raise APIError(
11, 'The address version number currently must be 2, 3 or 4.'
@@ -195,9 +205,11 @@ class MySimpleXMLRPCRequestHandler(SimpleXMLRPCRequestHandler):
# Request Handlers
def HandleListAddresses(self, method):
+ """Handle a request to list addresses"""
+
data = '{"addresses":['
for addressInKeysFile in BMConfigParser().addresses():
- status, addressVersionNumber, streamNumber, hash01 = decodeAddress(
+ status, addressVersionNumber, streamNumber, hash01 = decodeAddress( # pylint: disable=unused-variable
addressInKeysFile)
if len(data) > 20:
data += ','
@@ -215,11 +227,13 @@ class MySimpleXMLRPCRequestHandler(SimpleXMLRPCRequestHandler):
'enabled':
BMConfigParser().getboolean(addressInKeysFile, 'enabled'),
'chan': chan
- }, indent=4, separators=(',', ': '))
+ }, indent=4, separators=(',', ': '))
data += ']}'
return data
def HandleListAddressBookEntries(self, params):
+ """Handle a request to list address book entries"""
+
if len(params) == 1:
label, = params
label = self._decode(label, "base64")
@@ -243,6 +257,8 @@ class MySimpleXMLRPCRequestHandler(SimpleXMLRPCRequestHandler):
return data
def HandleAddAddressBookEntry(self, params):
+ """Handle a request to add an address book entry"""
+
if len(params) != 2:
raise APIError(0, "I need label and address")
address, label = params
@@ -262,6 +278,8 @@ class MySimpleXMLRPCRequestHandler(SimpleXMLRPCRequestHandler):
return "Added address %s to address book" % address
def HandleDeleteAddressBookEntry(self, params):
+ """Handle a request to delete an address book entry"""
+
if len(params) != 1:
raise APIError(0, "I need an address")
address, = params
@@ -274,8 +292,11 @@ class MySimpleXMLRPCRequestHandler(SimpleXMLRPCRequestHandler):
return "Deleted address book entry for %s if it existed" % address
def HandleCreateRandomAddress(self, params):
- if len(params) == 0:
+ """Handle a request to create a random address"""
+
+ if not params:
raise APIError(0, 'I need parameters!')
+
elif len(params) == 1:
label, = params
eighteenByteRipe = False
@@ -292,19 +313,16 @@ class MySimpleXMLRPCRequestHandler(SimpleXMLRPCRequestHandler):
elif len(params) == 3:
label, eighteenByteRipe, totalDifficulty = params
nonceTrialsPerByte = int(
- defaults.networkDefaultProofOfWorkNonceTrialsPerByte
- * totalDifficulty)
+ defaults.networkDefaultProofOfWorkNonceTrialsPerByte * totalDifficulty)
payloadLengthExtraBytes = BMConfigParser().get(
'bitmessagesettings', 'defaultpayloadlengthextrabytes')
elif len(params) == 4:
label, eighteenByteRipe, totalDifficulty, \
smallMessageDifficulty = params
nonceTrialsPerByte = int(
- defaults.networkDefaultProofOfWorkNonceTrialsPerByte
- * totalDifficulty)
+ defaults.networkDefaultProofOfWorkNonceTrialsPerByte * totalDifficulty)
payloadLengthExtraBytes = int(
- defaults.networkDefaultPayloadLengthExtraBytes
- * smallMessageDifficulty)
+ defaults.networkDefaultPayloadLengthExtraBytes * smallMessageDifficulty)
else:
raise APIError(0, 'Too many parameters!')
label = self._decode(label, "base64")
@@ -321,8 +339,11 @@ class MySimpleXMLRPCRequestHandler(SimpleXMLRPCRequestHandler):
return queues.apiAddressGeneratorReturnQueue.get()
def HandleCreateDeterministicAddresses(self, params):
- if len(params) == 0:
+ """Handle a request to create a deterministic address"""
+
+ if not params:
raise APIError(0, 'I need parameters!')
+
elif len(params) == 1:
passphrase, = params
numberOfAddresses = 1
@@ -333,6 +354,7 @@ class MySimpleXMLRPCRequestHandler(SimpleXMLRPCRequestHandler):
'bitmessagesettings', 'defaultnoncetrialsperbyte')
payloadLengthExtraBytes = BMConfigParser().get(
'bitmessagesettings', 'defaultpayloadlengthextrabytes')
+
elif len(params) == 2:
passphrase, numberOfAddresses = params
addressVersionNumber = 0
@@ -342,6 +364,7 @@ class MySimpleXMLRPCRequestHandler(SimpleXMLRPCRequestHandler):
'bitmessagesettings', 'defaultnoncetrialsperbyte')
payloadLengthExtraBytes = BMConfigParser().get(
'bitmessagesettings', 'defaultpayloadlengthextrabytes')
+
elif len(params) == 3:
passphrase, numberOfAddresses, addressVersionNumber = params
streamNumber = 0
@@ -350,6 +373,7 @@ class MySimpleXMLRPCRequestHandler(SimpleXMLRPCRequestHandler):
'bitmessagesettings', 'defaultnoncetrialsperbyte')
payloadLengthExtraBytes = BMConfigParser().get(
'bitmessagesettings', 'defaultpayloadlengthextrabytes')
+
elif len(params) == 4:
passphrase, numberOfAddresses, addressVersionNumber, \
streamNumber = params
@@ -358,6 +382,7 @@ class MySimpleXMLRPCRequestHandler(SimpleXMLRPCRequestHandler):
'bitmessagesettings', 'defaultnoncetrialsperbyte')
payloadLengthExtraBytes = BMConfigParser().get(
'bitmessagesettings', 'defaultpayloadlengthextrabytes')
+
elif len(params) == 5:
passphrase, numberOfAddresses, addressVersionNumber, \
streamNumber, eighteenByteRipe = params
@@ -365,27 +390,26 @@ class MySimpleXMLRPCRequestHandler(SimpleXMLRPCRequestHandler):
'bitmessagesettings', 'defaultnoncetrialsperbyte')
payloadLengthExtraBytes = BMConfigParser().get(
'bitmessagesettings', 'defaultpayloadlengthextrabytes')
+
elif len(params) == 6:
passphrase, numberOfAddresses, addressVersionNumber, \
streamNumber, eighteenByteRipe, totalDifficulty = params
nonceTrialsPerByte = int(
- defaults.networkDefaultProofOfWorkNonceTrialsPerByte
- * totalDifficulty)
+ defaults.networkDefaultProofOfWorkNonceTrialsPerByte * totalDifficulty)
payloadLengthExtraBytes = BMConfigParser().get(
'bitmessagesettings', 'defaultpayloadlengthextrabytes')
+
elif len(params) == 7:
passphrase, numberOfAddresses, addressVersionNumber, \
streamNumber, eighteenByteRipe, totalDifficulty, \
smallMessageDifficulty = params
nonceTrialsPerByte = int(
- defaults.networkDefaultProofOfWorkNonceTrialsPerByte
- * totalDifficulty)
+ defaults.networkDefaultProofOfWorkNonceTrialsPerByte * totalDifficulty)
payloadLengthExtraBytes = int(
- defaults.networkDefaultPayloadLengthExtraBytes
- * smallMessageDifficulty)
+ defaults.networkDefaultPayloadLengthExtraBytes * smallMessageDifficulty)
else:
raise APIError(0, 'Too many parameters!')
- if len(passphrase) == 0:
+ if not passphrase:
raise APIError(1, 'The specified passphrase is blank.')
if not isinstance(eighteenByteRipe, bool):
raise APIError(
@@ -436,12 +460,14 @@ class MySimpleXMLRPCRequestHandler(SimpleXMLRPCRequestHandler):
return data
def HandleGetDeterministicAddress(self, params):
+ """Handle a request to get a deterministic address"""
+
if len(params) != 3:
raise APIError(0, 'I need exactly 3 parameters.')
passphrase, addressVersionNumber, streamNumber = params
numberOfAddresses = 1
eighteenByteRipe = False
- if len(passphrase) == 0:
+ if not passphrase:
raise APIError(1, 'The specified passphrase is blank.')
passphrase = self._decode(passphrase, "base64")
if addressVersionNumber != 3 and addressVersionNumber != 4:
@@ -463,12 +489,16 @@ class MySimpleXMLRPCRequestHandler(SimpleXMLRPCRequestHandler):
return queues.apiAddressGeneratorReturnQueue.get()
def HandleCreateChan(self, params):
- if len(params) == 0:
+ """Handle a request to create a chan"""
+
+ if not params:
raise APIError(0, 'I need parameters.')
+
elif len(params) == 1:
passphrase, = params
passphrase = self._decode(passphrase, "base64")
- if len(passphrase) == 0:
+
+ if not passphrase:
raise APIError(1, 'The specified passphrase is blank.')
# It would be nice to make the label the passphrase but it is
# possible that the passphrase contains non-utf-8 characters.
@@ -488,18 +518,20 @@ class MySimpleXMLRPCRequestHandler(SimpleXMLRPCRequestHandler):
passphrase, True
))
queueReturn = queues.apiAddressGeneratorReturnQueue.get()
- if len(queueReturn) == 0:
+ if not queueReturn:
raise APIError(24, 'Chan address is already present.')
address = queueReturn[0]
return address
def HandleJoinChan(self, params):
+ """Handle a request to join a chan"""
+
if len(params) < 2:
raise APIError(0, 'I need two parameters.')
elif len(params) == 2:
passphrase, suppliedAddress = params
passphrase = self._decode(passphrase, "base64")
- if len(passphrase) == 0:
+ if not passphrase:
raise APIError(1, 'The specified passphrase is blank.')
# It would be nice to make the label the passphrase but it is
# possible that the passphrase contains non-utf-8 characters.
@@ -509,8 +541,8 @@ class MySimpleXMLRPCRequestHandler(SimpleXMLRPCRequestHandler):
except:
label = str_chan + ' ' + repr(passphrase)
- status, addressVersionNumber, streamNumber, toRipe = \
- self._verifyAddress(suppliedAddress)
+ status, addressVersionNumber, streamNumber, toRipe = self._verifyAddress( # pylint: disable=unused-variable
+ suppliedAddress)
suppliedAddress = addBMIfNotPresent(suppliedAddress)
queues.apiAddressGeneratorReturnQueue.queue.clear()
queues.addressGeneratorQueue.put((
@@ -522,20 +554,19 @@ class MySimpleXMLRPCRequestHandler(SimpleXMLRPCRequestHandler):
if addressGeneratorReturnValue[0] == \
'chan name does not match address':
raise APIError(18, 'Chan name does not match address.')
- if len(addressGeneratorReturnValue) == 0:
+ if not addressGeneratorReturnValue:
raise APIError(24, 'Chan address is already present.')
- # TODO: this variable is not used to anything
- # in case we ever want it for anything.
- # createdAddress = addressGeneratorReturnValue[0]
return "success"
def HandleLeaveChan(self, params):
- if len(params) == 0:
+ """Handle a request to leave a chan"""
+
+ if not params:
raise APIError(0, 'I need parameters.')
elif len(params) == 1:
address, = params
- status, addressVersionNumber, streamNumber, toRipe = \
- self._verifyAddress(address)
+ # pylint: disable=unused-variable
+ status, addressVersionNumber, streamNumber, toRipe = self._verifyAddress(address)
address = addBMIfNotPresent(address)
if not BMConfigParser().has_section(address):
raise APIError(
@@ -550,12 +581,14 @@ class MySimpleXMLRPCRequestHandler(SimpleXMLRPCRequestHandler):
return 'success'
def HandleDeleteAddress(self, params):
- if len(params) == 0:
+ """Handle a request to delete an address"""
+
+ if not params:
raise APIError(0, 'I need parameters.')
elif len(params) == 1:
address, = params
- status, addressVersionNumber, streamNumber, toRipe = \
- self._verifyAddress(address)
+ # pylint: disable=unused-variable
+ status, addressVersionNumber, streamNumber, toRipe = self._verifyAddress(address)
address = addBMIfNotPresent(address)
if not BMConfigParser().has_section(address):
raise APIError(
@@ -568,7 +601,9 @@ class MySimpleXMLRPCRequestHandler(SimpleXMLRPCRequestHandler):
shared.reloadMyAddressHashes()
return 'success'
- def HandleGetAllInboxMessages(self, params):
+ def HandleGetAllInboxMessages(self, params): # pylint: disable=unused-argument
+ """Handle a request to get all inbox messages"""
+
queryreturn = sqlQuery(
"SELECT msgid, toaddress, fromaddress, subject, received, message,"
" encodingtype, read FROM inbox where folder='inbox'"
@@ -594,7 +629,9 @@ class MySimpleXMLRPCRequestHandler(SimpleXMLRPCRequestHandler):
data += ']}'
return data
- def HandleGetAllInboxMessageIds(self, params):
+ def HandleGetAllInboxMessageIds(self, params): # pylint: disable=unused-argument
+ """Handle a request to get all inbox message IDs"""
+
queryreturn = sqlQuery(
"SELECT msgid FROM inbox where folder='inbox' ORDER BY received")
data = '{"inboxMessageIds":['
@@ -608,7 +645,9 @@ class MySimpleXMLRPCRequestHandler(SimpleXMLRPCRequestHandler):
return data
def HandleGetInboxMessageById(self, params):
- if len(params) == 0:
+ """Handle a request to get an inbox messsage by ID"""
+
+ if not params:
raise APIError(0, 'I need parameters!')
elif len(params) == 1:
msgid = self._decode(params[0], "hex")
@@ -649,7 +688,9 @@ class MySimpleXMLRPCRequestHandler(SimpleXMLRPCRequestHandler):
data += ']}'
return data
- def HandleGetAllSentMessages(self, params):
+ def HandleGetAllSentMessages(self, params): # pylint: disable=unused-argument
+ """Handle a request to get all sent messages"""
+
queryreturn = sqlQuery(
"SELECT msgid, toaddress, fromaddress, subject, lastactiontime,"
" message, encodingtype, status, ackdata FROM sent"
@@ -676,7 +717,9 @@ class MySimpleXMLRPCRequestHandler(SimpleXMLRPCRequestHandler):
data += ']}'
return data
- def HandleGetAllSentMessageIds(self, params):
+ def HandleGetAllSentMessageIds(self, params): # pylint: disable=unused-argument
+ """Handle a request to get all sent message IDs"""
+
queryreturn = sqlQuery(
"SELECT msgid FROM sent where folder='sent'"
" ORDER BY lastactiontime"
@@ -692,7 +735,9 @@ class MySimpleXMLRPCRequestHandler(SimpleXMLRPCRequestHandler):
return data
def HandleInboxMessagesByReceiver(self, params):
- if len(params) == 0:
+ """Handle a request to get inbox messages by receiver"""
+
+ if not params:
raise APIError(0, 'I need parameters!')
toAddress = params[0]
queryreturn = sqlQuery(
@@ -719,7 +764,9 @@ class MySimpleXMLRPCRequestHandler(SimpleXMLRPCRequestHandler):
return data
def HandleGetSentMessageById(self, params):
- if len(params) == 0:
+ """Handle a request to get a sent message by ID"""
+
+ if not params:
raise APIError(0, 'I need parameters!')
msgid = self._decode(params[0], "hex")
queryreturn = sqlQuery(
@@ -747,7 +794,9 @@ class MySimpleXMLRPCRequestHandler(SimpleXMLRPCRequestHandler):
return data
def HandleGetSentMessagesByAddress(self, params):
- if len(params) == 0:
+ """Handle a request to get sent messages by address"""
+
+ if not params:
raise APIError(0, 'I need parameters!')
fromAddress = params[0]
queryreturn = sqlQuery(
@@ -759,7 +808,7 @@ class MySimpleXMLRPCRequestHandler(SimpleXMLRPCRequestHandler):
data = '{"sentMessages":['
for row in queryreturn:
msgid, toAddress, fromAddress, subject, lastactiontime, message, \
- encodingtype, status, ackdata = row
+ encodingtype, status, ackdata = row # pylint: disable=unused-variable
subject = shared.fixPotentiallyInvalidUTF8Data(subject)
message = shared.fixPotentiallyInvalidUTF8Data(message)
if len(data) > 25:
@@ -778,7 +827,9 @@ class MySimpleXMLRPCRequestHandler(SimpleXMLRPCRequestHandler):
return data
def HandleGetSentMessagesByAckData(self, params):
- if len(params) == 0:
+ """Handle a request to get sent messages by ack data"""
+
+ if not params:
raise APIError(0, 'I need parameters!')
ackData = self._decode(params[0], "hex")
queryreturn = sqlQuery(
@@ -806,7 +857,9 @@ class MySimpleXMLRPCRequestHandler(SimpleXMLRPCRequestHandler):
return data
def HandleTrashMessage(self, params):
- if len(params) == 0:
+ """Handle a request to trash a message by ID"""
+
+ if not params:
raise APIError(0, 'I need parameters!')
msgid = self._decode(params[0], "hex")
@@ -817,32 +870,42 @@ class MySimpleXMLRPCRequestHandler(SimpleXMLRPCRequestHandler):
return 'Trashed message (assuming message existed).'
def HandleTrashInboxMessage(self, params):
- if len(params) == 0:
+ """Handle a request to trash an inbox message by ID"""
+
+ if not params:
raise APIError(0, 'I need parameters!')
msgid = self._decode(params[0], "hex")
helper_inbox.trash(msgid)
return 'Trashed inbox message (assuming message existed).'
def HandleTrashSentMessage(self, params):
- if len(params) == 0:
+ """Handle a request to trash a sent message by ID"""
+
+ if not params:
raise APIError(0, 'I need parameters!')
msgid = self._decode(params[0], "hex")
sqlExecute('''UPDATE sent SET folder='trash' WHERE msgid=?''', msgid)
return 'Trashed sent message (assuming message existed).'
def HandleSendMessage(self, params):
- if len(params) == 0:
+ """Handle a request to send a message"""
+
+ if not params:
raise APIError(0, 'I need parameters!')
+
elif len(params) == 4:
toAddress, fromAddress, subject, message = params
encodingType = 2
TTL = 4 * 24 * 60 * 60
+
elif len(params) == 5:
toAddress, fromAddress, subject, message, encodingType = params
TTL = 4 * 24 * 60 * 60
+
elif len(params) == 6:
toAddress, fromAddress, subject, message, encodingType, TTL = \
params
+
if encodingType not in [2, 3]:
raise APIError(6, 'The encoding type must be 2 or 3.')
subject = self._decode(subject, "base64")
@@ -855,6 +918,7 @@ class MySimpleXMLRPCRequestHandler(SimpleXMLRPCRequestHandler):
TTL = 28 * 24 * 60 * 60
toAddress = addBMIfNotPresent(toAddress)
fromAddress = addBMIfNotPresent(fromAddress)
+ # pylint: disable=unused-variable
status, addressVersionNumber, streamNumber, toRipe = \
self._verifyAddress(toAddress)
self._verifyAddress(fromAddress)
@@ -894,7 +958,6 @@ class MySimpleXMLRPCRequestHandler(SimpleXMLRPCRequestHandler):
if queryreturn != []:
for row in queryreturn:
toLabel, = row
- # apiSignalQueue.put(('displayNewSentMessage',(toAddress,toLabel,fromAddress,subject,message,ackdata)))
queues.UISignalQueue.put(('displayNewSentMessage', (
toAddress, toLabel, fromAddress, subject, message, ackdata)))
@@ -903,19 +966,25 @@ class MySimpleXMLRPCRequestHandler(SimpleXMLRPCRequestHandler):
return hexlify(ackdata)
def HandleSendBroadcast(self, params):
- if len(params) == 0:
+ """Handle a request to send a broadcast message"""
+
+ if not params:
raise APIError(0, 'I need parameters!')
+
if len(params) == 3:
fromAddress, subject, message = params
encodingType = 2
TTL = 4 * 24 * 60 * 60
+
elif len(params) == 4:
fromAddress, subject, message, encodingType = params
TTL = 4 * 24 * 60 * 60
elif len(params) == 5:
fromAddress, subject, message, encodingType, TTL = params
+
if encodingType not in [2, 3]:
raise APIError(6, 'The encoding type must be 2 or 3.')
+
subject = self._decode(subject, "base64")
message = self._decode(message, "base64")
if len(subject + message) > (2 ** 18 - 500):
@@ -961,6 +1030,8 @@ class MySimpleXMLRPCRequestHandler(SimpleXMLRPCRequestHandler):
return hexlify(ackdata)
def HandleGetStatus(self, params):
+ """Handle a request to get the status of a sent message"""
+
if len(params) != 1:
raise APIError(0, 'I need one parameter!')
ackdata, = params
@@ -977,7 +1048,9 @@ class MySimpleXMLRPCRequestHandler(SimpleXMLRPCRequestHandler):
return status
def HandleAddSubscription(self, params):
- if len(params) == 0:
+ """Handle a request to add a subscription"""
+
+ if not params:
raise APIError(0, 'I need parameters!')
if len(params) == 1:
address, = params
@@ -1007,6 +1080,8 @@ class MySimpleXMLRPCRequestHandler(SimpleXMLRPCRequestHandler):
return 'Added subscription.'
def HandleDeleteSubscription(self, params):
+ """Handle a request to delete a subscription"""
+
if len(params) != 1:
raise APIError(0, 'I need 1 parameter!')
address, = params
@@ -1017,7 +1092,10 @@ class MySimpleXMLRPCRequestHandler(SimpleXMLRPCRequestHandler):
queues.UISignalQueue.put(('rerenderSubscriptions', ''))
return 'Deleted subscription if it existed.'
- def ListSubscriptions(self, params):
+ def ListSubscriptions(self, params): # pylint: disable=unused-argument
+ """Handle a request to list susbcriptions"""
+
+ # pylint: disable=unused-variable
queryreturn = sqlQuery(
"SELECT label, address, enabled FROM subscriptions")
data = {'subscriptions': []}
@@ -1032,6 +1110,8 @@ class MySimpleXMLRPCRequestHandler(SimpleXMLRPCRequestHandler):
return json.dumps(data, indent=4, separators=(',', ': '))
def HandleDisseminatePreEncryptedMsg(self, params):
+ """Handle a request to disseminate an encrypted message"""
+
# The device issuing this command to PyBitmessage supplies a msg
# object that has already been encrypted but which still needs the POW
# to be done. PyBitmessage accepts this msg object and sends it out
@@ -1044,17 +1124,29 @@ class MySimpleXMLRPCRequestHandler(SimpleXMLRPCRequestHandler):
encryptedPayload = self._decode(encryptedPayload, "hex")
# Let us do the POW and attach it to the front
target = 2**64 / (
- (len(encryptedPayload) + requiredPayloadLengthExtraBytes + 8)
- * requiredAverageProofOfWorkNonceTrialsPerByte)
+ (
+ len(encryptedPayload) + requiredPayloadLengthExtraBytes + 8
+ ) * requiredAverageProofOfWorkNonceTrialsPerByte
+ )
with shared.printLock:
- print '(For msg message via API) Doing proof of work. Total required difficulty:', float(requiredAverageProofOfWorkNonceTrialsPerByte) / defaults.networkDefaultProofOfWorkNonceTrialsPerByte, 'Required small message difficulty:', float(requiredPayloadLengthExtraBytes) / defaults.networkDefaultPayloadLengthExtraBytes
+ print(
+ '(For msg message via API) Doing proof of work. Total required difficulty:',
+ float(
+ requiredAverageProofOfWorkNonceTrialsPerByte
+ ) / defaults.networkDefaultProofOfWorkNonceTrialsPerByte,
+ 'Required small message difficulty:',
+ float(requiredPayloadLengthExtraBytes) / defaults.networkDefaultPayloadLengthExtraBytes,
+ )
powStartTime = time.time()
initialHash = hashlib.sha512(encryptedPayload).digest()
trialValue, nonce = proofofwork.run(target, initialHash)
with shared.printLock:
print '(For msg message via API) Found proof of work', trialValue, 'Nonce:', nonce
try:
- print 'POW took', int(time.time() - powStartTime), 'seconds.', nonce / (time.time() - powStartTime), 'nonce trials per second.'
+ print(
+ 'POW took', int(time.time() - powStartTime), 'seconds.',
+ nonce / (time.time() - powStartTime), 'nonce trials per second.',
+ )
except:
pass
encryptedPayload = pack('>Q', nonce) + encryptedPayload
@@ -1071,14 +1163,18 @@ class MySimpleXMLRPCRequestHandler(SimpleXMLRPCRequestHandler):
queues.invQueue.put((toStreamNumber, inventoryHash))
def HandleTrashSentMessageByAckDAta(self, params):
+ """Handle a request to trash a sent message by ackdata"""
+
# This API method should only be used when msgid is not available
- if len(params) == 0:
+ if not params:
raise APIError(0, 'I need parameters!')
ackdata = self._decode(params[0], "hex")
sqlExecute("UPDATE sent SET folder='trash' WHERE ackdata=?", ackdata)
return 'Trashed sent message (assuming message existed).'
- def HandleDissimatePubKey(self, params):
+ def HandleDissimatePubKey(self, params): # pylint: disable=unused-argument
+ """Handle a request to disseminate a public key"""
+
# The device issuing this command to PyBitmessage supplies a pubkey
# object to be disseminated to the rest of the Bitmessage network.
# PyBitmessage accepts this pubkey object and sends it out to the rest
@@ -1090,9 +1186,9 @@ class MySimpleXMLRPCRequestHandler(SimpleXMLRPCRequestHandler):
payload = self._decode(payload, "hex")
# Let us do the POW
- target = 2 ** 64 / (
- (len(payload) + defaults.networkDefaultPayloadLengthExtraBytes
- + 8) * defaults.networkDefaultProofOfWorkNonceTrialsPerByte)
+ target = 2 ** 64 / ((
+ len(payload) + defaults.networkDefaultPayloadLengthExtraBytes + 8
+ ) * defaults.networkDefaultProofOfWorkNonceTrialsPerByte)
print '(For pubkey message via API) Doing proof of work...'
initialHash = hashlib.sha512(payload).digest()
trialValue, nonce = proofofwork.run(target, initialHash)
@@ -1100,18 +1196,19 @@ class MySimpleXMLRPCRequestHandler(SimpleXMLRPCRequestHandler):
payload = pack('>Q', nonce) + payload
pubkeyReadPosition = 8 # bypass the nonce
- if payload[pubkeyReadPosition:pubkeyReadPosition+4] == \
+ if payload[pubkeyReadPosition:pubkeyReadPosition + 4] == \
'\x00\x00\x00\x00': # if this pubkey uses 8 byte time
pubkeyReadPosition += 8
else:
pubkeyReadPosition += 4
+ # pylint: disable=unused-variable
addressVersion, addressVersionLength = decodeVarint(
- payload[pubkeyReadPosition:pubkeyReadPosition+10])
+ payload[pubkeyReadPosition:pubkeyReadPosition + 10])
pubkeyReadPosition += addressVersionLength
pubkeyStreamNumber = decodeVarint(
- payload[pubkeyReadPosition:pubkeyReadPosition+10])[0]
+ payload[pubkeyReadPosition:pubkeyReadPosition + 10])[0]
inventoryHash = calculateInventoryHash(payload)
- objectType = 1 # TODO: support v4 pubkeys
+ objectType = 1 # .. todo::: support v4 pubkeys
TTL = 28 * 24 * 60 * 60
Inventory()[inventoryHash] = (
objectType, pubkeyStreamNumber, payload, int(time.time()) + TTL, ''
@@ -1121,6 +1218,8 @@ class MySimpleXMLRPCRequestHandler(SimpleXMLRPCRequestHandler):
queues.invQueue.put((pubkeyStreamNumber, inventoryHash))
def HandleGetMessageDataByDestinationHash(self, params):
+ """Handle a request to get message data by destination hash"""
+
# Method will eventually be used by a particular Android app to
# select relevant messages. Do not yet add this to the api
# doc.
@@ -1145,8 +1244,8 @@ class MySimpleXMLRPCRequestHandler(SimpleXMLRPCRequestHandler):
readPosition = 16 # Nonce length + time length
# Stream Number length
readPosition += decodeVarint(
- payload[readPosition:readPosition+10])[1]
- t = (payload[readPosition:readPosition+32], hash01)
+ payload[readPosition:readPosition + 10])[1]
+ t = (payload[readPosition:readPosition + 32], hash01)
sql.execute("UPDATE inventory SET tag=? WHERE hash=?", *t)
queryreturn = sqlQuery(
@@ -1161,10 +1260,12 @@ class MySimpleXMLRPCRequestHandler(SimpleXMLRPCRequestHandler):
data += ']}'
return data
- def HandleClientStatus(self, params):
- if len(network.stats.connectedHostsList()) == 0:
+ def HandleClientStatus(self, params): # pylint: disable=unused-argument
+ """Handle a request to get the status of the client"""
+
+ if not network.stats.connectedHostsList():
networkStatus = 'notConnected'
- elif len(network.stats.connectedHostsList()) > 0 \
+ elif not network.stats.connectedHostsList() \
and not shared.clientHasReceivedIncomingConnections:
networkStatus = 'connectedButHaveNotReceivedIncomingConnections'
else:
@@ -1177,9 +1278,11 @@ class MySimpleXMLRPCRequestHandler(SimpleXMLRPCRequestHandler):
'networkStatus': networkStatus,
'softwareName': 'PyBitmessage',
'softwareVersion': softwareVersion
- }, indent=4, separators=(',', ': '))
+ }, indent=4, separators=(',', ': '))
def HandleDecodeAddress(self, params):
+ """Handle a request to decode an address"""
+
# Return a meaningful decoding of an address.
if len(params) != 1:
raise APIError(0, 'I need 1 parameter!')
@@ -1190,29 +1293,41 @@ class MySimpleXMLRPCRequestHandler(SimpleXMLRPCRequestHandler):
'addressVersion': addressVersion,
'streamNumber': streamNumber,
'ripe': base64.b64encode(ripe)
- }, indent=4, separators=(',', ': '))
+ }, indent=4, separators=(',', ': '))
def HandleHelloWorld(self, params):
+ """Test two string params"""
+
a, b = params
return a + '-' + b
def HandleAdd(self, params):
+ """Test two numeric params"""
+
a, b = params
return a + b
def HandleStatusBar(self, params):
+ """Handle a request to update the status bar"""
+
message, = params
queues.UISignalQueue.put(('updateStatusBar', message))
def HandleDeleteAndVacuum(self, params):
+ """Handle a request to run the deleteandvacuum stored procedure"""
+
if not params:
sqlStoredProcedure('deleteandvacuume')
return 'done'
+ return None
def HandleShutdown(self, params):
+ """Handle a request to huutdown the client"""
+
if not params:
shutdown.doCleanShutdown()
return 'done'
+ return None
handlers = {}
handlers['helloWorld'] = HandleHelloWorld
@@ -1279,6 +1394,7 @@ class MySimpleXMLRPCRequestHandler(SimpleXMLRPCRequestHandler):
return self.handlers[method](self, params)
def _dispatch(self, method, params):
+ # pylint: disable=attribute-defined-outside-init
self.cookies = []
validuser = self.APIAuthenticateClient()
diff --git a/src/bitmessagemain.py b/src/bitmessagemain.py
index a9cb7986..3a116709 100755
--- a/src/bitmessagemain.py
+++ b/src/bitmessagemain.py
@@ -1,37 +1,40 @@
#!/usr/bin/python2.7
-# Copyright (c) 2012-2016 Jonathan Warren
-# Copyright (c) 2012-2018 The Bitmessage developers
-# Distributed under the MIT/X11 software license. See the accompanying
-# file COPYING or http://www.opensource.org/licenses/mit-license.php.
+# pylint: disable=no-self-use,too-many-branches,too-many-statements,too-many-locals
+"""
+bitmessagemain.py
+=================
-# Right now, PyBitmessage only support connecting to stream 1. It doesn't
-# yet contain logic to expand into further streams.
+Copyright (c) 2012-2016 Jonathan Warren
+Copyright (c) 2012-2018 The Bitmessage developers
+Distributed under the MIT/X11 software license. See the accompanying
+file COPYING or http://www.opensource.org/licenses/mit-license.php.
-# The software version variable is now held in shared.py
+Right now, PyBitmessage only support connecting to stream 1. It doesn't
+yet contain logic to expand into further streams.
-import os
-import sys
+The software version variable is now held in shared.py
-app_dir = os.path.dirname(os.path.abspath(__file__))
-os.chdir(app_dir)
-sys.path.insert(0, app_dir)
+"""
+from __future__ import absolute_import
-import depends
-depends.check_dependencies()
-
-# Used to capture a Ctrl-C keypress so that Bitmessage can shutdown gracefully.
-import signal
-# The next 3 are used for the API
-from singleinstance import singleinstance
-import errno
-import socket
import ctypes
+import errno
+import getopt
+import os
+import signal
+import socket
+import sys
+import threading
+from random import randint
from struct import pack
from subprocess import call
from time import sleep
-from random import randint
-import getopt
+
+
+# Used to capture a Ctrl-C keypress so that Bitmessage can shutdown gracefully.
+# The next 3 are used for the API
+from singleinstance import singleinstance
from api import MySimpleXMLRPCRequestHandler, StoppableXMLRPCServer
from helper_startup import (
@@ -39,11 +42,11 @@ from helper_startup import (
)
import defaults
+import depends
import shared
import knownnodes
import state
import shutdown
-import threading
# Classes
from class_sqlThread import sqlThread
@@ -72,7 +75,12 @@ import helper_generic
import helper_threading
+depends.check_dependencies()
+
+
def connectToStream(streamNumber):
+ """Connect to a stream"""
+
state.streamsInWhichIAmParticipating.append(streamNumber)
selfInitiatedConnections[streamNumber] = {}
@@ -93,10 +101,10 @@ def connectToStream(streamNumber):
with knownnodes.knownNodesLock:
if streamNumber not in knownnodes.knownNodes:
knownnodes.knownNodes[streamNumber] = {}
- if streamNumber*2 not in knownnodes.knownNodes:
- knownnodes.knownNodes[streamNumber*2] = {}
- if streamNumber*2+1 not in knownnodes.knownNodes:
- knownnodes.knownNodes[streamNumber*2+1] = {}
+ if streamNumber * 2 not in knownnodes.knownNodes:
+ knownnodes.knownNodes[streamNumber * 2] = {}
+ if streamNumber * 2 + 1 not in knownnodes.knownNodes:
+ knownnodes.knownNodes[streamNumber * 2 + 1] = {}
BMConnectionPool().connectToStream(streamNumber)
@@ -114,6 +122,8 @@ def _fixSocket():
addressToString = ctypes.windll.ws2_32.WSAAddressToStringA
def inet_ntop(family, host):
+ """Convert IPv4 and IPv6 addresses from binary to text form"""
+
if family == socket.AF_INET:
if len(host) != 4:
raise ValueError("invalid IPv4 host")
@@ -135,6 +145,8 @@ def _fixSocket():
stringToAddress = ctypes.windll.ws2_32.WSAStringToAddressA
def inet_pton(family, host):
+ """Convert IPv4 and IPv6 addresses from text to binary form"""
+
buf = "\0" * 28
lengthBuf = pack("I", len(buf))
if stringToAddress(str(host),
@@ -158,13 +170,15 @@ def _fixSocket():
socket.IPV6_V6ONLY = 27
-# This thread, of which there is only one, runs the API.
class singleAPI(threading.Thread, helper_threading.StoppableThread):
+ """This thread, of which there is only one, runs the API."""
+
def __init__(self):
threading.Thread.__init__(self, name="singleAPI")
self.initStop()
def stopThread(self):
+ """Stop the API thread"""
super(singleAPI, self).stopThread()
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
try:
@@ -178,9 +192,10 @@ class singleAPI(threading.Thread, helper_threading.StoppableThread):
pass
def run(self):
+ """Run the API thread"""
port = BMConfigParser().getint('bitmessagesettings', 'apiport')
try:
- from errno import WSAEADDRINUSE
+ from errno import WSAEADDRINUSE # pylint: disable=unused-variable
except (ImportError, AttributeError):
errno.WSAEADDRINUSE = errno.EADDRINUSE
for attempt in range(50):
@@ -215,15 +230,18 @@ if shared.useVeryEasyProofOfWorkForTesting:
defaults.networkDefaultPayloadLengthExtraBytes / 100)
-class Main:
+class Main(object):
+ """The main app"""
+
def start(self):
+ """Start the main app"""
_fixSocket()
daemon = BMConfigParser().safeGetBoolean(
'bitmessagesettings', 'daemon')
try:
- opts, args = getopt.getopt(
+ opts, _ = getopt.getopt(
sys.argv[1:], "hcdt",
["help", "curses", "daemon", "test"])
@@ -231,7 +249,7 @@ class Main:
self.usage()
sys.exit(2)
- for opt, arg in opts:
+ for opt, _ in opts:
if opt in ("-h", "--help"):
self.usage()
sys.exit()
@@ -249,7 +267,7 @@ class Main:
if daemon and not state.testmode:
with shared.printLock:
- print('Running as a daemon. Send TERM signal to end.')
+ print 'Running as a daemon. Send TERM signal to end.'
self.daemonize()
self.setSignalHandler()
@@ -381,8 +399,7 @@ class Main:
BMConfigParser().remove_option('bitmessagesettings', 'dontconnect')
elif daemon is False:
if state.curses:
- # if depends.check_curses():
- print('Running with curses')
+ print 'Running with curses'
import bitmessagecurses
bitmessagecurses.runwrapper()
elif depends.check_pyqt():
@@ -411,6 +428,7 @@ class Main:
sleep(1)
def daemonize(self):
+ """Daemonise"""
grandfatherPid = os.getpid()
parentPid = None
try:
@@ -420,7 +438,7 @@ class Main:
# wait until grandchild ready
while True:
sleep(1)
- os._exit(0)
+ sys.exit(0)
except AttributeError:
# fork not implemented
pass
@@ -441,7 +459,7 @@ class Main:
# wait until child ready
while True:
sleep(1)
- os._exit(0)
+ sys.exit(0)
except AttributeError:
# fork not implemented
pass
@@ -463,11 +481,13 @@ class Main:
os.kill(grandfatherPid, signal.SIGTERM)
def setSignalHandler(self):
+ """Register signal handlers"""
signal.signal(signal.SIGINT, helper_generic.signal_handler)
signal.signal(signal.SIGTERM, helper_generic.signal_handler)
- # signal.signal(signal.SIGINT, signal.SIG_DFL)
def usage(self):
+ """Print usage message"""
+
print 'Usage: ' + sys.argv[0] + ' [OPTIONS]'
print '''
Options:
@@ -480,12 +500,18 @@ All parameters are optional.
'''
def stop(self):
+ """Stop the daemon"""
with shared.printLock:
- print('Stopping Bitmessage Deamon.')
+ print 'Stopping Bitmessage Daemon.'
shutdown.doCleanShutdown()
- # TODO: nice function but no one is using this
def getApiAddress(self):
+ """
+ Return the address and port the API is configured to use
+
+ .. todo:: nice function but no one is using this
+ """
+
if not BMConfigParser().safeGetBoolean(
'bitmessagesettings', 'apienabled'):
return None
@@ -495,14 +521,10 @@ All parameters are optional.
def main():
+ """Create and start the main app"""
mainprogram = Main()
mainprogram.start()
if __name__ == "__main__":
main()
-
-
-# So far, the creation of and management of the Bitmessage protocol and this
-# client is a one-man operation. Bitcoin tips are quite appreciated.
-# 1H5XaDA6fYENLbknwZyjiYXYPQaFjjLX2u
diff --git a/src/bitmessageqt/account.py b/src/bitmessageqt/account.py
index 92d497f8..cf3373db 100644
--- a/src/bitmessageqt/account.py
+++ b/src/bitmessageqt/account.py
@@ -1,26 +1,58 @@
-from PyQt4 import QtCore, QtGui
+# pylint: disable=too-many-instance-attributes,attribute-defined-outside-init
+"""
+account.py
+==========
-import queues
+Account related functions.
+
+"""
+
+from __future__ import absolute_import
+
+import inspect
import re
import sys
-import inspect
-from helper_sql import *
-from helper_ackPayload import genAckPayload
-from addresses import decodeAddress
-from bmconfigparser import BMConfigParser
-from foldertree import AccountMixin
-from pyelliptic.openssl import OpenSSL
-from utils import str_broadcast_subscribers
import time
+from PyQt4 import QtGui
+
+import queues
+from addresses import decodeAddress
+from bmconfigparser import BMConfigParser
+from helper_ackPayload import genAckPayload
+from helper_sql import sqlQuery, sqlExecute
+from .foldertree import AccountMixin
+from .utils import str_broadcast_subscribers
+
+
def getSortedAccounts():
+ """Get a sorted list of configSections"""
+
configSections = BMConfigParser().addresses()
- configSections.sort(cmp =
- lambda x,y: cmp(unicode(BMConfigParser().get(x, 'label'), 'utf-8').lower(), unicode(BMConfigParser().get(y, 'label'), 'utf-8').lower())
- )
+ configSections.sort(
+ cmp=lambda x, y: cmp(
+ unicode(
+ BMConfigParser().get(
+ x,
+ 'label'),
+ 'utf-8').lower(),
+ unicode(
+ BMConfigParser().get(
+ y,
+ 'label'),
+ 'utf-8').lower()))
return configSections
-def getSortedSubscriptions(count = False):
+
+def getSortedSubscriptions(count=False):
+ """
+ Actually return a grouped dictionary rather than a sorted list
+
+ :param count: Whether to count messages for each fromaddress in the inbox
+ :type count: bool, default 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')
ret = {}
for row in queryreturn:
@@ -37,7 +69,7 @@ def getSortedSubscriptions(count = False):
GROUP BY inbox.fromaddress, folder''', str_broadcast_subscribers)
for row in queryreturn:
address, folder, cnt = row
- if not folder in ret[address]:
+ if folder not in ret[address]:
ret[address][folder] = {
'label': ret[address]['inbox']['label'],
'enabled': ret[address]['inbox']['enabled']
@@ -45,9 +77,11 @@ def getSortedSubscriptions(count = False):
ret[address][folder]['count'] = cnt
return ret
+
def accountClass(address):
+ """Return a BMAccount for the address"""
if not BMConfigParser().has_section(address):
- # FIXME: This BROADCAST section makes no sense
+ # .. todo:: This BROADCAST section makes no sense
if address == str_broadcast_subscribers:
subscription = BroadcastAccount(address)
if subscription.type != AccountMixin.BROADCAST:
@@ -60,8 +94,7 @@ def accountClass(address):
return subscription
try:
gateway = BMConfigParser().get(address, "gateway")
- for name, cls in inspect.getmembers(sys.modules[__name__], inspect.isclass):
-# obj = g(address)
+ for _, cls in inspect.getmembers(sys.modules[__name__], inspect.isclass):
if issubclass(cls, GatewayAccount) and cls.gatewayName == gateway:
return cls(address)
# general gateway
@@ -70,12 +103,15 @@ def accountClass(address):
pass
# no gateway
return BMAccount(address)
-
-class AccountColor(AccountMixin):
- def __init__(self, address, type = None):
+
+
+class AccountColor(AccountMixin): # pylint: disable=too-few-public-methods
+ """Set the type of account"""
+
+ def __init__(self, address, address_type=None):
self.isEnabled = True
self.address = address
- if type is None:
+ if address_type is None:
if address is None:
self.type = AccountMixin.ALL
elif BMConfigParser().safeGetBoolean(self.address, 'mailinglist'):
@@ -83,16 +119,18 @@ class AccountColor(AccountMixin):
elif BMConfigParser().safeGetBoolean(self.address, 'chan'):
self.type = AccountMixin.CHAN
elif sqlQuery(
- '''select label from subscriptions where address=?''', self.address):
+ '''select label from subscriptions where address=?''', self.address):
self.type = AccountMixin.SUBSCRIPTION
else:
self.type = AccountMixin.NORMAL
else:
- self.type = type
+ self.type = address_type
+
-
class BMAccount(object):
- def __init__(self, address = None):
+ """Encapsulate a Bitmessage account"""
+
+ def __init__(self, address=None):
self.address = address
self.type = AccountMixin.NORMAL
if BMConfigParser().has_section(address):
@@ -108,7 +146,8 @@ class BMAccount(object):
if queryreturn:
self.type = AccountMixin.SUBSCRIPTION
- def getLabel(self, address = None):
+ def getLabel(self, address=None):
+ """Get a label for this bitmessage account"""
if address is None:
address = self.address
label = address
@@ -126,8 +165,10 @@ class BMAccount(object):
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):
@@ -140,36 +181,45 @@ class BMAccount(object):
class NoAccount(BMAccount):
- def __init__(self, address = None):
+ """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):
+ def getLabel(self, address=None):
if address is None:
address = self.address
return address
-
+
class SubscriptionAccount(BMAccount):
+ """Encapsulate a subscription account"""
pass
-
+
class BroadcastAccount(BMAccount):
+ """Encapsulate a broadcast account"""
pass
-
-
+
+
class GatewayAccount(BMAccount):
+ """Encapsulate a gateway account"""
+
gatewayName = None
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 = BMConfigParser().safeGetInt('bitmessagesettings', 'ackstealthlevel')
ackdata = genAckPayload(streamNumber, stealthLevel)
- t = ()
sqlExecute(
'''INSERT INTO sent VALUES (?,?,?,?,?,?,?,?,?,?,?,?,?,?,?)''',
'',
@@ -179,47 +229,52 @@ class GatewayAccount(BMAccount):
self.subject,
self.message,
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.
+ 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',
- 0, # retryNumber
- 'sent', # folder
- 2, # encodingtype
- min(BMConfigParser().getint('bitmessagesettings', 'ttl'), 86400 * 2) # not necessary to have a TTL higher than 2 days
+ 0, # retryNumber
+ 'sent', # folder
+ 2, # encodingtype
+ # not necessary to have a TTL higher than 2 days
+ min(BMConfigParser().getint('bitmessagesettings', 'ttl'), 86400 * 2)
)
queues.workerQueue.put(('sendmessage', self.toAddress))
-
- def parseMessage(self, toAddress, fromAddress, subject, message):
- super(GatewayAccount, self).parseMessage(toAddress, fromAddress, subject, message)
+
class MailchuckAccount(GatewayAccount):
+ """Encapsulate a particular kind of gateway account"""
+
# set "gateway" in keys.dat to this
gatewayName = "mailchuck"
registrationAddress = "BM-2cVYYrhaY5Gbi3KqrX9Eae2NRNrkfrhCSA"
unregistrationAddress = "BM-2cVMAHTRjZHCTPMue75XBK5Tco175DtJ9J"
relayAddress = "BM-2cWim8aZwUNqxzjMxstnUMtVEUQJeezstf"
- regExpIncoming = re.compile("(.*)MAILCHUCK-FROM::(\S+) \| (.*)")
- regExpOutgoing = re.compile("(\S+) (.*)")
+ regExpIncoming = re.compile(r"(.*)MAILCHUCK-FROM::(\S+) \| (.*)")
+ regExpOutgoing = re.compile(r"(\S+) (.*)")
+
def __init__(self, address):
super(MailchuckAccount, self).__init__(address)
self.feedback = self.ALL_OK
-
+
def createMessage(self, toAddress, fromAddress, subject, message):
+ """createMessage specific to a MailchuckAccount"""
self.subject = toAddress + " " + subject
self.toAddress = self.relayAddress
self.fromAddress = fromAddress
self.message = message
-
+
def register(self, email):
+ """register specific to a MailchuckAccount"""
self.toAddress = self.registrationAddress
self.subject = email
self.message = ""
self.fromAddress = self.address
self.send()
-
+
def unregister(self):
+ """unregister specific to a MailchuckAccount"""
self.toAddress = self.unregistrationAddress
self.subject = ""
self.message = ""
@@ -227,6 +282,7 @@ class MailchuckAccount(GatewayAccount):
self.send()
def status(self):
+ """status specific to a MailchuckAccount"""
self.toAddress = self.registrationAddress
self.subject = "status"
self.message = ""
@@ -234,12 +290,16 @@ class MailchuckAccount(GatewayAccount):
self.send()
def settings(self):
+ """settings specific to a MailchuckAccount"""
+
self.toAddress = self.registrationAddress
self.subject = "config"
- self.message = QtGui.QApplication.translate("Mailchuck", """# You can use this to configure your email gateway account
+ self.message = QtGui.QApplication.translate(
+ "Mailchuck",
+ """# You can use this to configure your email gateway account
# Uncomment the setting you want to use
# Here are the options:
-#
+#
# pgp: server
# The email gateway will create and maintain PGP keys for you and sign, verify,
# encrypt and decrypt on your behalf. When you want to use PGP but are lazy,
@@ -255,7 +315,7 @@ class MailchuckAccount(GatewayAccount):
#
# attachments: no
# Attachments will be ignored.
-#
+#
# archive: yes
# Your incoming emails will be archived on the server. Use this if you need
# help with debugging problems or you need a third party proof of emails. This
@@ -279,10 +339,12 @@ class MailchuckAccount(GatewayAccount):
self.fromAddress = self.address
def parseMessage(self, toAddress, fromAddress, subject, message):
+ """parseMessage specific to a MailchuckAccount"""
+
super(MailchuckAccount, self).parseMessage(toAddress, fromAddress, subject, message)
if fromAddress == self.relayAddress:
matches = self.regExpIncoming.search(subject)
- if not matches is None:
+ if matches is not None:
self.subject = ""
if not matches.group(1) is None:
self.subject += matches.group(1)
@@ -293,7 +355,7 @@ class MailchuckAccount(GatewayAccount):
self.fromAddress = matches.group(2)
if toAddress == self.relayAddress:
matches = self.regExpOutgoing.search(subject)
- if not matches is None:
+ if matches is not None:
if not matches.group(2) is None:
self.subject = matches.group(2)
if not matches.group(1) is None:
diff --git a/src/bitmessageqt/bitmessageui.py b/src/bitmessageqt/bitmessageui.py
index cb3578c0..7aec31f2 100644
--- a/src/bitmessageqt/bitmessageui.py
+++ b/src/bitmessageqt/bitmessageui.py
@@ -1,20 +1,29 @@
# -*- coding: utf-8 -*-
+# pylint: disable=too-many-locals
+"""
+Form implementation generated from reading ui file 'bitmessageui.ui'
-# 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!
+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 __future__ import absolute_import
+
+import sys
from PyQt4 import QtCore, QtGui
+
from bmconfigparser import BMConfigParser
-from foldertree import AddressBookCompleter
-from messageview import MessageView
-from messagecompose import MessageCompose
-import settingsmixin
-from networkstatus import NetworkStatus
-from blacklist import Blacklist
+from . import bitmessage_icons_rc # pylint: disable=unused-import
+from . import settingsmixin
+from .messageview import MessageView
+from .messagecompose import MessageCompose
+from .networkstatus import NetworkStatus
+from .blacklist import Blacklist
+from .foldertree import AddressBookCompleter
+
try:
_fromUtf8 = QtCore.QString.fromUtf8
@@ -24,24 +33,38 @@ except AttributeError:
try:
_encoding = QtGui.QApplication.UnicodeUTF8
- def _translate(context, text, disambig, encoding = QtCore.QCoreApplication.CodecForTr, n = None):
+
+ def _translate(context, text, disambig, encoding=QtCore.QCoreApplication.CodecForTr, n=None):
+ # pylint: disable=unused-argument
if n is None:
return QtGui.QApplication.translate(context, text, disambig, _encoding)
- else:
- return QtGui.QApplication.translate(context, text, disambig, _encoding, n)
+ return QtGui.QApplication.translate(context, text, disambig, _encoding, n)
+
except AttributeError:
- def _translate(context, text, disambig, encoding = QtCore.QCoreApplication.CodecForTr, n = None):
+
+ def _translate(context, text, disambig, encoding=QtCore.QCoreApplication.CodecForTr, n=None):
+ # pylint: disable=unused-argument
if n is None:
return QtGui.QApplication.translate(context, text, disambig)
- else:
- return QtGui.QApplication.translate(context, text, disambig, QtCore.QCoreApplication.CodecForTr, n)
+ return QtGui.QApplication.translate(context, text, disambig, QtCore.QCoreApplication.CodecForTr, n)
+
class Ui_MainWindow(object):
+ """Encapsulate the main UI"""
+ # pylint: disable=too-many-instance-attributes,too-many-statements
+
def setupUi(self, MainWindow):
+ """Set up the UI"""
+ # pylint: disable=attribute-defined-outside-init
+
MainWindow.setObjectName(_fromUtf8("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(
+ _fromUtf8(":/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)
@@ -75,7 +98,11 @@ class Ui_MainWindow(object):
self.treeWidgetYourIdentities.setObjectName(_fromUtf8("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(
+ _fromUtf8(":/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)
@@ -175,7 +202,11 @@ class Ui_MainWindow(object):
self.tableWidgetAddressBook.resize(200, self.tableWidgetAddressBook.height())
item = QtGui.QTableWidgetItem()
icon3 = QtGui.QIcon()
- icon3.addPixmap(QtGui.QPixmap(_fromUtf8(":/newPrefix/images/addressbook.png")), QtGui.QIcon.Selected, QtGui.QIcon.Off)
+ icon3.addPixmap(
+ QtGui.QPixmap(
+ _fromUtf8(":/newPrefix/images/addressbook.png")),
+ QtGui.QIcon.Selected,
+ QtGui.QIcon.Off)
item.setIcon(icon3)
self.tableWidgetAddressBook.setHorizontalHeaderItem(0, item)
item = QtGui.QTableWidgetItem()
@@ -376,7 +407,11 @@ class Ui_MainWindow(object):
self.treeWidgetSubscriptions.setObjectName(_fromUtf8("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(
+ _fromUtf8(":/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)
@@ -455,7 +490,11 @@ 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)
+ 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"))
@@ -475,7 +514,11 @@ class Ui_MainWindow(object):
self.treeWidgetChans.setObjectName(_fromUtf8("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(
+ _fromUtf8(":/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)
@@ -554,7 +597,11 @@ 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)
+ icon8.addPixmap(
+ QtGui.QPixmap(
+ _fromUtf8(":/newPrefix/images/can-icon-16px.png")),
+ QtGui.QIcon.Normal,
+ QtGui.QIcon.Off)
self.tabWidget.addTab(self.chans, icon8, _fromUtf8(""))
self.blackwhitelist = Blacklist()
self.tabWidget.addTab(self.blackwhitelist, QtGui.QIcon(":/newPrefix/images/blacklist.png"), "")
@@ -652,6 +699,8 @@ class Ui_MainWindow(object):
MainWindow.setTabOrder(self.textEditMessage, self.pushButtonAddSubscription)
def updateNetworkSwitchMenuLabel(self, dontconnect=None):
+ """Restore last online/offline setting"""
+
if dontconnect is None:
dontconnect = BMConfigParser().safeGetBoolean(
'bitmessagesettings', 'dontconnect')
@@ -662,6 +711,8 @@ class Ui_MainWindow(object):
)
def retranslateUi(self, MainWindow):
+ """Re-translate the UI"""
+
MainWindow.setWindowTitle(_translate("MainWindow", "Bitmessage", None))
self.treeWidgetYourIdentities.headerItem().setText(0, _translate("MainWindow", "Identities", None))
self.pushButtonNewAddress.setText(_translate("MainWindow", "New Identity", None))
@@ -691,19 +742,33 @@ class Ui_MainWindow(object):
self.label_3.setText(_translate("MainWindow", "Subject:", None))
self.label_2.setText(_translate("MainWindow", "From:", None))
self.label.setText(_translate("MainWindow", "To:", None))
- #self.textEditMessage.setHtml("")
- self.tabWidgetSend.setTabText(self.tabWidgetSend.indexOf(self.sendDirect), _translate("MainWindow", "Send ordinary Message", None))
+ # self.textEditMessage.setHtml("")
+ self.tabWidgetSend.setTabText(
+ self.tabWidgetSend.indexOf(
+ self.sendDirect),
+ _translate(
+ "MainWindow", "Send ordinary Message", None))
self.label_8.setText(_translate("MainWindow", "From:", None))
self.label_7.setText(_translate("MainWindow", "Subject:", None))
- #self.textEditMessageBroadcast.setHtml("")
- self.tabWidgetSend.setTabText(self.tabWidgetSend.indexOf(self.sendBroadcast), _translate("MainWindow", "Send Message to your Subscribers", None))
+ # self.textEditMessageBroadcast.setHtml("")
+ self.tabWidgetSend.setTabText(
+ self.tabWidgetSend.indexOf(
+ self.sendBroadcast),
+ _translate(
+ "MainWindow", "Send Message to your Subscribers", None))
self.pushButtonTTL.setText(_translate("MainWindow", "TTL:", None))
hours = 48
try:
- hours = int(BMConfigParser().getint('bitmessagesettings', 'ttl')/60/60)
+ hours = int(BMConfigParser().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,
+ QtCore.QCoreApplication.CodecForTr,
+ 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))
@@ -724,7 +789,11 @@ class Ui_MainWindow(object):
item.setText(_translate("MainWindow", "Subject", None))
item = self.tableWidgetInboxSubscriptions.horizontalHeaderItem(3)
item.setText(_translate("MainWindow", "Received", None))
- self.tabWidget.setTabText(self.tabWidget.indexOf(self.subscriptions), _translate("MainWindow", "Subscriptions", None))
+ self.tabWidget.setTabText(
+ self.tabWidget.indexOf(
+ self.subscriptions),
+ _translate(
+ "MainWindow", "Subscriptions", None))
self.treeWidgetChans.headerItem().setText(0, _translate("MainWindow", "Chans", None))
self.pushButtonAddChan.setText(_translate("MainWindow", "Add Chan", None))
self.inboxSearchLineEditChans.setPlaceholderText(_translate("MainWindow", "Search", None))
@@ -744,9 +813,17 @@ class Ui_MainWindow(object):
item.setText(_translate("MainWindow", "Received", None))
self.tabWidget.setTabText(self.tabWidget.indexOf(self.chans), _translate("MainWindow", "Chans", None))
self.blackwhitelist.retranslateUi()
- self.tabWidget.setTabText(self.tabWidget.indexOf(self.blackwhitelist), _translate("blacklist", "Blacklist", None))
+ self.tabWidget.setTabText(
+ self.tabWidget.indexOf(
+ self.blackwhitelist),
+ _translate(
+ "blacklist", "Blacklist", None))
self.networkstatus.retranslateUi()
- self.tabWidget.setTabText(self.tabWidget.indexOf(self.networkstatus), _translate("networkstatus", "Network Status", None))
+ self.tabWidget.setTabText(
+ self.tabWidget.indexOf(
+ self.networkstatus),
+ _translate(
+ "networkstatus", "Network Status", None))
self.menuFile.setTitle(_translate("MainWindow", "File", None))
self.menuSettings.setTitle(_translate("MainWindow", "Settings", None))
self.menuHelp.setTitle(_translate("MainWindow", "Help", None))
@@ -759,19 +836,17 @@ class Ui_MainWindow(object):
self.actionSupport.setText(_translate("MainWindow", "Contact support", None))
self.actionAbout.setText(_translate("MainWindow", "About", None))
self.actionSettings.setText(_translate("MainWindow", "Settings", None))
- self.actionRegenerateDeterministicAddresses.setText(_translate("MainWindow", "Regenerate deterministic addresses", None))
+ self.actionRegenerateDeterministicAddresses.setText(
+ _translate("MainWindow", "Regenerate deterministic addresses", None))
self.actionDeleteAllTrashedMessages.setText(_translate("MainWindow", "Delete all trashed messages", None))
self.actionJoinChan.setText(_translate("MainWindow", "Join / Create chan", None))
-import bitmessage_icons_rc
if __name__ == "__main__":
- import sys
-
- app = QtGui.QApplication(sys.argv)
- MainWindow = settingsmixin.SMainWindow()
- ui = Ui_MainWindow()
- ui.setupUi(MainWindow)
- MainWindow.show()
- sys.exit(app.exec_())
+ app = QtGui.QApplication(sys.argv)
+ ThisMainWindow = settingsmixin.SMainWindow()
+ ui = Ui_MainWindow()
+ ui.setupUi(ThisMainWindow)
+ ThisMainWindow.show()
+ sys.exit(app.exec_())
diff --git a/src/bitmessageqt/newaddresswizard.py b/src/bitmessageqt/newaddresswizard.py
index 2311239c..1cc2bef1 100644
--- a/src/bitmessageqt/newaddresswizard.py
+++ b/src/bitmessageqt/newaddresswizard.py
@@ -1,12 +1,26 @@
#!/usr/bin/env python2.7
+# pylint: disable=no-self-use
+"""
+newaddresswizard.py
+===================
+"""
+
+import sys
+import time
+
from PyQt4 import QtCore, QtGui
+
class NewAddressWizardIntroPage(QtGui.QWizardPage):
+ """The introduction page for the new address wizard"""
+
def __init__(self):
- super(QtGui.QWizardPage, self).__init__()
+ super(NewAddressWizardIntroPage, self).__init__()
self.setTitle("Creating a new address")
- label = QtGui.QLabel("This wizard will help you create as many addresses as you like. Indeed, creating and abandoning addresses is encouraged.\n\n"
+ label = QtGui.QLabel(
+ "This wizard will help you create as many addresses as you like."
+ " Indeed, creating and abandoning addresses is encouraged.\n\n"
"What type of address would you like? Would you like to send emails or not?\n"
"You can still change your mind later, and register/unregister with an email service provider.\n\n")
label.setWordWrap(True)
@@ -22,30 +36,35 @@ class NewAddressWizardIntroPage(QtGui.QWizardPage):
layout.addWidget(self.emailAsWell)
layout.addWidget(self.onlyBM)
self.setLayout(layout)
-
+
def nextId(self):
+ """Page 1 or 4"""
+
if self.emailAsWell.isChecked():
return 4
- else:
- return 1
-
+ return 1
+
class NewAddressWizardRngPassphrasePage(QtGui.QWizardPage):
+ """The user chose a random or passphrase-based address"""
+
def __init__(self):
- super(QtGui.QWizardPage, self).__init__()
+ super(NewAddressWizardRngPassphrasePage, self).__init__()
self.setTitle("Random or Passphrase")
- label = QtGui.QLabel("
You may generate addresses by using either random numbers or by using a passphrase. "
- "If you use a passphrase, the address is called a "deterministic" address. "
- "The \'Random Number\' option is selected by default but deterministic addresses have several pros and cons:
"
- "Pros: | Cons: |
"
- "You can recreate your addresses on any computer from memory. "
- "You need-not worry about backing up your keys.dat file as long as you can remember your passphrase. | "
- "You must remember (or write down) your passphrase if you expect to be able "
- "to recreate your keys if they are lost. "
-# "You must remember the address version number and the stream number along with your passphrase. "
- "If you choose a weak passphrase and someone on the Internet can brute-force it, they can read your messages and send messages as you."
- "")
+ label = QtGui.QLabel(
+ " | You may generate addresses by using either random numbers or by using a passphrase."
+ " If you use a passphrase, the address is called a "deterministic" address."
+ " The \'Random Number\' option is selected by default but deterministic addresses have several pros and"
+ " cons:
Pros: | "
+ " Cons: |
"
+ " You can recreate your addresses on any computer from memory."
+ " You need-not worry about backing up your keys.dat file as long as you can remember your passphrase. | "
+ " You must remember (or write down) your passphrase if you expect to be able"
+ " to recreate your keys if they are lost."
+ # "You must remember the address version number and the stream number along with your passphrase. "
+ " If you choose a weak passphrase and someone on the Internet can brute-force it, they can read your"
+ " messages and send messages as you.")
label.setWordWrap(True)
self.randomAddress = QtGui.QRadioButton("Use a random number generator to make an address")
@@ -59,14 +78,18 @@ class NewAddressWizardRngPassphrasePage(QtGui.QWizardPage):
self.setLayout(layout)
def nextId(self):
+ """Page 2"""
+
if self.randomAddress.isChecked():
return 2
- else:
- return 3
+ return 3
+
class NewAddressWizardRandomPage(QtGui.QWizardPage):
+ """The user chose a new random address"""
+
def __init__(self, addresses):
- super(QtGui.QWizardPage, self).__init__()
+ super(NewAddressWizardRandomPage, self).__init__()
self.setTitle("Random")
label = QtGui.QLabel("Random address.")
@@ -75,10 +98,11 @@ class NewAddressWizardRandomPage(QtGui.QWizardPage):
labelLabel = QtGui.QLabel("Label (not shown to anyone except you):")
self.labelLineEdit = QtGui.QLineEdit()
- self.radioButtonMostAvailable = QtGui.QRadioButton("Use the most available stream\n"
+ self.radioButtonMostAvailable = QtGui.QRadioButton(
+ "Use the most available stream\n"
"(best if this is the first of many addresses you will create)")
self.radioButtonExisting = QtGui.QRadioButton("Use the same stream as an existing address\n"
- "(saves you some bandwidth and processing power)")
+ "(saves you some bandwidth and processing power)")
self.radioButtonMostAvailable.setChecked(True)
self.comboBoxExisting = QtGui.QComboBox()
self.comboBoxExisting.setEnabled(False)
@@ -86,10 +110,10 @@ class NewAddressWizardRandomPage(QtGui.QWizardPage):
for address in addresses:
self.comboBoxExisting.addItem(address)
-
-# self.comboBoxExisting.setObjectName(_fromUtf8("comboBoxExisting"))
- self.checkBoxEighteenByteRipe = QtGui.QCheckBox("Spend several minutes of extra computing time to make the address(es) 1 or 2 characters shorter")
-
+
+ self.checkBoxEighteenByteRipe = QtGui.QCheckBox(
+ "Spend several minutes of extra computing time to make the address(es) 1 or 2 characters shorter")
+
layout = QtGui.QGridLayout()
layout.addWidget(label, 0, 0)
layout.addWidget(labelLabel, 1, 0)
@@ -100,24 +124,27 @@ class NewAddressWizardRandomPage(QtGui.QWizardPage):
layout.addWidget(self.checkBoxEighteenByteRipe, 6, 0)
self.setLayout(layout)
- QtCore.QObject.connect(self.radioButtonExisting, QtCore.SIGNAL("toggled(bool)"), self.comboBoxExisting.setEnabled)
-
+ QtCore.QObject.connect( # pylint: disable=no-member
+ self.radioButtonExisting,
+ QtCore.SIGNAL("toggled(bool)"),
+ self.comboBoxExisting.setEnabled)
+
self.registerField("label", self.labelLineEdit)
self.registerField("radioButtonMostAvailable", self.radioButtonMostAvailable)
self.registerField("radioButtonExisting", self.radioButtonExisting)
self.registerField("comboBoxExisting", self.comboBoxExisting)
-# self.emailAsWell = QtGui.QRadioButton("Combined email and bitmessage account")
-# self.onlyBM = QtGui.QRadioButton("Bitmessage-only account (no email)")
-# self.emailAsWell.setChecked(True)
-
def nextId(self):
+ """Page 6"""
+
return 6
-
+
class NewAddressWizardPassphrasePage(QtGui.QWizardPage):
+ """The user chose a passphrase-based address"""
+
def __init__(self):
- super(QtGui.QWizardPage, self).__init__()
+ super(NewAddressWizardPassphrasePage, self).__init__()
self.setTitle("Passphrase")
label = QtGui.QLabel("Deterministric address.")
@@ -126,7 +153,8 @@ class NewAddressWizardPassphrasePage(QtGui.QWizardPage):
passphraseLabel = QtGui.QLabel("Passphrase")
self.lineEditPassphrase = QtGui.QLineEdit()
self.lineEditPassphrase.setEchoMode(QtGui.QLineEdit.Password)
- self.lineEditPassphrase.setInputMethodHints(QtCore.Qt.ImhHiddenText|QtCore.Qt.ImhNoAutoUppercase|QtCore.Qt.ImhNoPredictiveText)
+ self.lineEditPassphrase.setInputMethodHints(
+ QtCore.Qt.ImhHiddenText | QtCore.Qt.ImhNoAutoUppercase | QtCore.Qt.ImhNoPredictiveText)
retypePassphraseLabel = QtGui.QLabel("Retype passphrase")
self.lineEditPassphraseAgain = QtGui.QLineEdit()
self.lineEditPassphraseAgain.setEchoMode(QtGui.QLineEdit.Password)
@@ -135,11 +163,11 @@ class NewAddressWizardPassphrasePage(QtGui.QWizardPage):
self.spinBoxNumberOfAddressesToMake = QtGui.QSpinBox()
self.spinBoxNumberOfAddressesToMake.setMinimum(1)
self.spinBoxNumberOfAddressesToMake.setProperty("value", 8)
-# self.spinBoxNumberOfAddressesToMake.setObjectName(_fromUtf8("spinBoxNumberOfAddressesToMake"))
+
label2 = QtGui.QLabel("In addition to your passphrase, you must remember these numbers:")
label3 = QtGui.QLabel("Address version number: 4")
label4 = QtGui.QLabel("Stream number: 1")
-
+
layout = QtGui.QGridLayout()
layout.addWidget(label, 0, 0, 1, 4)
layout.addWidget(passphraseLabel, 1, 0, 1, 4)
@@ -155,34 +183,39 @@ class NewAddressWizardPassphrasePage(QtGui.QWizardPage):
self.setLayout(layout)
def nextId(self):
+ """Page 6"""
+
return 6
-
+
class NewAddressWizardEmailProviderPage(QtGui.QWizardPage):
+ """The user choses the email gateway address type"""
+
def __init__(self):
- super(QtGui.QWizardPage, self).__init__()
+ super(NewAddressWizardEmailProviderPage, self).__init__()
self.setTitle("Choose email provider")
label = QtGui.QLabel("Currently only Mailchuck email gateway is available "
- "(@mailchuck.com email address). In the future, maybe other gateways will be available. "
- "Press Next.")
+ "(@mailchuck.com email address). In the future, maybe other gateways will be available. "
+ "Press Next.")
label.setWordWrap(True)
-# self.mailchuck = QtGui.QRadioButton("Mailchuck email gateway (@mailchuck.com)")
-# self.mailchuck.setChecked(True)
-
layout = QtGui.QVBoxLayout()
layout.addWidget(label)
-# layout.addWidget(self.mailchuck)
+
self.setLayout(layout)
def nextId(self):
+ """Page 5"""
+
return 5
-
+
class NewAddressWizardEmailAddressPage(QtGui.QWizardPage):
+ """The user provides their email gateway detauils"""
+
def __init__(self):
- super(QtGui.QWizardPage, self).__init__()
+ super(NewAddressWizardEmailAddressPage, self).__init__()
self.setTitle("Email address")
label = QtGui.QLabel("Choosing an email address. Address must end with @mailchuck.com")
@@ -192,8 +225,9 @@ class NewAddressWizardEmailAddressPage(QtGui.QWizardPage):
self.specificEmail.setChecked(True)
self.emailLineEdit = QtGui.QLineEdit()
self.randomEmail = QtGui.QRadioButton("Generate a random email address")
-
- QtCore.QObject.connect(self.specificEmail, QtCore.SIGNAL("toggled(bool)"), self.emailLineEdit.setEnabled)
+
+ QtCore.QObject.connect( # pylint: disable=no-member
+ self.specificEmail, QtCore.SIGNAL("toggled(bool)"), self.emailLineEdit.setEnabled)
layout = QtGui.QVBoxLayout()
layout.addWidget(label)
@@ -203,33 +237,37 @@ class NewAddressWizardEmailAddressPage(QtGui.QWizardPage):
self.setLayout(layout)
def nextId(self):
+ """Page 6"""
+
return 6
-
+
class NewAddressWizardWaitPage(QtGui.QWizardPage):
+ """Wait for the address to be generated"""
+
def __init__(self):
- super(QtGui.QWizardPage, self).__init__()
+ super(NewAddressWizardWaitPage, self).__init__()
self.setTitle("Wait")
-
+
self.label = QtGui.QLabel("Wait!")
self.label.setWordWrap(True)
self.progressBar = QtGui.QProgressBar()
self.progressBar.setMinimum(0)
self.progressBar.setMaximum(100)
self.progressBar.setValue(0)
-
-# self.emailAsWell = QtGui.QRadioButton("Combined email and bitmessage account")
-# self.onlyBM = QtGui.QRadioButton("Bitmessage-only account (no email)")
-# self.emailAsWell.setChecked(True)
layout = QtGui.QVBoxLayout()
layout.addWidget(self.label)
layout.addWidget(self.progressBar)
-# layout.addWidget(self.emailAsWell)
-# layout.addWidget(self.onlyBM)
+
self.setLayout(layout)
def update(self, i):
+ """
+ Update the progress bar
+
+ .. todo:: remove print statement?
+ """
if i == 101 and self.wizard().currentId() == 6:
self.wizard().button(QtGui.QWizard.NextButton).click()
return
@@ -239,15 +277,17 @@ class NewAddressWizardWaitPage(QtGui.QWizardPage):
self.progressBar.setValue(i)
if i == 50:
self.emit(QtCore.SIGNAL('completeChanged()'))
-
+
def isComplete(self):
-# print "val = " + str(self.progressBar.value())
+ """Predicate to indicate progress is complete"""
+
if self.progressBar.value() >= 50:
return True
- else:
- return False
-
+ return False
+
def initializePage(self):
+ """Initialize the underlying QWizardPage"""
+
if self.field("emailAsWell").toBool():
val = "yes/"
else:
@@ -258,19 +298,23 @@ class NewAddressWizardWaitPage(QtGui.QWizardPage):
val += "no"
self.label.setText("Wait! " + val)
-# self.wizard().button(QtGui.QWizard.NextButton).setEnabled(False)
+
self.progressBar.setValue(0)
self.thread = NewAddressThread()
self.connect(self.thread, self.thread.signal, self.update)
self.thread.start()
-
+
def nextId(self):
+ """Page 10"""
+
return 10
-
+
class NewAddressWizardConclusionPage(QtGui.QWizardPage):
+ """The user is informed their address has been created"""
+
def __init__(self):
- super(QtGui.QWizardPage, self).__init__()
+ super(NewAddressWizardConclusionPage, self).__init__()
self.setTitle("All done!")
label = QtGui.QLabel("You successfully created a new address.")
@@ -278,14 +322,17 @@ class NewAddressWizardConclusionPage(QtGui.QWizardPage):
layout = QtGui.QVBoxLayout()
layout.addWidget(label)
- self.setLayout(layout)
+ self.setLayout(layout)
+
class Ui_NewAddressWizard(QtGui.QWizard):
+ """The wizard is a collection of pages"""
+
def __init__(self, addresses):
- super(QtGui.QWizard, self).__init__()
+ super(Ui_NewAddressWizard, self).__init__()
self.pages = {}
-
+
page = NewAddressWizardIntroPage()
self.setPage(0, page)
self.setStartId(0)
@@ -308,45 +355,45 @@ class Ui_NewAddressWizard(QtGui.QWizard):
self.adjustSize()
self.show()
+
class NewAddressThread(QtCore.QThread):
+ # pylint: disable=missing-docstring
+
def __init__(self):
QtCore.QThread.__init__(self)
self.signal = QtCore.SIGNAL("signal")
def __del__(self):
self.wait()
-
+
def createDeterministic(self):
pass
-
+
def createPassphrase(self):
pass
-
+
def broadcastAddress(self):
pass
-
+
def registerMailchuck(self):
pass
-
+
def waitRegistration(self):
pass
def run(self):
- import time
for i in range(1, 101):
- time.sleep(0.1) # artificial time delay
+ time.sleep(0.1) # artificial time delay
self.emit(self.signal, i)
self.emit(self.signal, 101)
-# self.terminate()
+
if __name__ == '__main__':
- import sys
-
app = QtGui.QApplication(sys.argv)
wizard = Ui_NewAddressWizard(["a", "b", "c", "d"])
- if (wizard.exec_()):
+ if wizard.exec_():
print "Email: " + ("yes" if wizard.field("emailAsWell").toBool() else "no")
print "BM: " + ("yes" if wizard.field("onlyBM").toBool() else "no")
else:
diff --git a/src/bitmessageqt/settings.py b/src/bitmessageqt/settings.py
index 4342fd09..22c38a62 100644
--- a/src/bitmessageqt/settings.py
+++ b/src/bitmessageqt/settings.py
@@ -1,15 +1,23 @@
# -*- coding: utf-8 -*-
+# pylint: disable=too-many-instance-attributes,too-many-locals,too-many-statements,attribute-defined-outside-init
+"""
+Form implementation generated from reading ui file 'settings.ui'
-# Form implementation generated from reading ui file 'settings.ui'
-#
-# Created: Thu Dec 25 23:21:20 2014
-# by: PyQt4 UI code generator 4.10.3
-#
-# WARNING! All changes made in this file will be lost!
+Created: Thu Dec 25 23:21:20 2014
+ by: PyQt4 UI code generator 4.10.3
+
+WARNING! All changes made in this file will be lost!
+"""
+
+from __future__ import absolute_import
+
+from sys import platform
from PyQt4 import QtCore, QtGui
-from languagebox import LanguageBox
-from sys import platform
+
+from . import bitmessage_icons_rc # pylint: disable=unused-import
+from .languagebox import LanguageBox
+
try:
_fromUtf8 = QtCore.QString.fromUtf8
@@ -19,21 +27,27 @@ except AttributeError:
try:
_encoding = QtGui.QApplication.UnicodeUTF8
+
def _translate(context, text, disambig):
return QtGui.QApplication.translate(context, text, disambig, _encoding)
except AttributeError:
def _translate(context, text, disambig):
return QtGui.QApplication.translate(context, text, disambig)
+
class Ui_settingsDialog(object):
+ """Encapsulate a UI settings dialog object"""
+
def setupUi(self, settingsDialog):
+ """Set up the UI"""
+
settingsDialog.setObjectName(_fromUtf8("settingsDialog"))
settingsDialog.resize(521, 413)
self.gridLayout = QtGui.QGridLayout(settingsDialog)
self.gridLayout.setObjectName(_fromUtf8("gridLayout"))
self.buttonBox = QtGui.QDialogButtonBox(settingsDialog)
self.buttonBox.setOrientation(QtCore.Qt.Horizontal)
- self.buttonBox.setStandardButtons(QtGui.QDialogButtonBox.Cancel|QtGui.QDialogButtonBox.Ok)
+ self.buttonBox.setStandardButtons(QtGui.QDialogButtonBox.Cancel | QtGui.QDialogButtonBox.Ok)
self.buttonBox.setObjectName(_fromUtf8("buttonBox"))
self.gridLayout.addWidget(self.buttonBox, 1, 0, 1, 1)
self.tabWidgetSettings = QtGui.QTabWidget(settingsDialog)
@@ -64,7 +78,8 @@ class Ui_settingsDialog(object):
self.formLayout.setWidget(1, QtGui.QFormLayout.SpanningRole, self.groupBoxTray)
self.checkBoxHideTrayConnectionNotifications = QtGui.QCheckBox(self.tabUserInterface)
self.checkBoxHideTrayConnectionNotifications.setChecked(False)
- self.checkBoxHideTrayConnectionNotifications.setObjectName(_fromUtf8("checkBoxHideTrayConnectionNotifications"))
+ self.checkBoxHideTrayConnectionNotifications.setObjectName(
+ _fromUtf8("checkBoxHideTrayConnectionNotifications"))
self.formLayout.setWidget(2, QtGui.QFormLayout.LabelRole, self.checkBoxHideTrayConnectionNotifications)
self.checkBoxShowTrayNotifications = QtGui.QCheckBox(self.tabUserInterface)
self.checkBoxShowTrayNotifications.setObjectName(_fromUtf8("checkBoxShowTrayNotifications"))
@@ -96,7 +111,7 @@ class Ui_settingsDialog(object):
self.formLayout_2.setObjectName(_fromUtf8("formLayout_2"))
self.languageComboBox = LanguageBox(self.groupBox)
self.languageComboBox.setMinimumSize(QtCore.QSize(100, 0))
- self.languageComboBox.setObjectName(_fromUtf8("languageComboBox"))
+ self.languageComboBox.setObjectName(_fromUtf8("languageComboBox")) # pylint: disable=not-callable
self.formLayout_2.setWidget(0, QtGui.QFormLayout.LabelRole, self.languageComboBox)
self.formLayout.setWidget(9, QtGui.QFormLayout.FieldRole, self.groupBox)
self.tabWidgetSettings.addTab(self.tabUserInterface, _fromUtf8(""))
@@ -108,8 +123,6 @@ class Ui_settingsDialog(object):
self.groupBox1.setObjectName(_fromUtf8("groupBox1"))
self.gridLayout_3 = QtGui.QGridLayout(self.groupBox1)
self.gridLayout_3.setObjectName(_fromUtf8("gridLayout_3"))
- #spacerItem = QtGui.QSpacerItem(125, 20, QtGui.QSizePolicy.Expanding, QtGui.QSizePolicy.Minimum)
- #self.gridLayout_3.addItem(spacerItem, 0, 0, 1, 1)
self.label = QtGui.QLabel(self.groupBox1)
self.label.setObjectName(_fromUtf8("label"))
self.gridLayout_3.addWidget(self.label, 0, 0, 1, 1, QtCore.Qt.AlignRight)
@@ -165,7 +178,8 @@ class Ui_settingsDialog(object):
self.lineEditMaxOutboundConnections.setSizePolicy(sizePolicy)
self.lineEditMaxOutboundConnections.setMaximumSize(QtCore.QSize(60, 16777215))
self.lineEditMaxOutboundConnections.setObjectName(_fromUtf8("lineEditMaxOutboundConnections"))
- self.lineEditMaxOutboundConnections.setValidator(QtGui.QIntValidator(0, 8, self.lineEditMaxOutboundConnections))
+ self.lineEditMaxOutboundConnections.setValidator(
+ QtGui.QIntValidator(0, 8, self.lineEditMaxOutboundConnections))
self.gridLayout_9.addWidget(self.lineEditMaxOutboundConnections, 2, 2, 1, 1)
self.gridLayout_4.addWidget(self.groupBox_3, 2, 0, 1, 1)
self.groupBox_2 = QtGui.QGroupBox(self.tabNetworkSettings)
@@ -207,7 +221,8 @@ class Ui_settingsDialog(object):
self.gridLayout_2.addWidget(self.label_6, 2, 4, 1, 1)
self.lineEditSocksPassword = QtGui.QLineEdit(self.groupBox_2)
self.lineEditSocksPassword.setEnabled(False)
- self.lineEditSocksPassword.setInputMethodHints(QtCore.Qt.ImhHiddenText|QtCore.Qt.ImhNoAutoUppercase|QtCore.Qt.ImhNoPredictiveText)
+ self.lineEditSocksPassword.setInputMethodHints(
+ QtCore.Qt.ImhHiddenText | QtCore.Qt.ImhNoAutoUppercase | QtCore.Qt.ImhNoPredictiveText)
self.lineEditSocksPassword.setEchoMode(QtGui.QLineEdit.Password)
self.lineEditSocksPassword.setObjectName(_fromUtf8("lineEditSocksPassword"))
self.gridLayout_2.addWidget(self.lineEditSocksPassword, 2, 5, 1, 1)
@@ -215,7 +230,7 @@ class Ui_settingsDialog(object):
self.checkBoxSocksListen.setObjectName(_fromUtf8("checkBoxSocksListen"))
self.gridLayout_2.addWidget(self.checkBoxSocksListen, 3, 1, 1, 4)
self.comboBoxProxyType = QtGui.QComboBox(self.groupBox_2)
- self.comboBoxProxyType.setObjectName(_fromUtf8("comboBoxProxyType"))
+ self.comboBoxProxyType.setObjectName(_fromUtf8("comboBoxProxyType")) # pylint: disable=not-callable
self.comboBoxProxyType.addItem(_fromUtf8(""))
self.comboBoxProxyType.addItem(_fromUtf8(""))
self.comboBoxProxyType.addItem(_fromUtf8(""))
@@ -229,7 +244,7 @@ class Ui_settingsDialog(object):
self.gridLayout_6 = QtGui.QGridLayout(self.tabDemandedDifficulty)
self.gridLayout_6.setObjectName(_fromUtf8("gridLayout_6"))
self.label_9 = QtGui.QLabel(self.tabDemandedDifficulty)
- self.label_9.setAlignment(QtCore.Qt.AlignRight|QtCore.Qt.AlignTrailing|QtCore.Qt.AlignVCenter)
+ self.label_9.setAlignment(QtCore.Qt.AlignRight | QtCore.Qt.AlignTrailing | QtCore.Qt.AlignVCenter)
self.label_9.setObjectName(_fromUtf8("label_9"))
self.gridLayout_6.addWidget(self.label_9, 1, 1, 1, 1)
self.label_10 = QtGui.QLabel(self.tabDemandedDifficulty)
@@ -237,7 +252,7 @@ class Ui_settingsDialog(object):
self.label_10.setObjectName(_fromUtf8("label_10"))
self.gridLayout_6.addWidget(self.label_10, 2, 0, 1, 3)
self.label_11 = QtGui.QLabel(self.tabDemandedDifficulty)
- self.label_11.setAlignment(QtCore.Qt.AlignRight|QtCore.Qt.AlignTrailing|QtCore.Qt.AlignVCenter)
+ self.label_11.setAlignment(QtCore.Qt.AlignRight | QtCore.Qt.AlignTrailing | QtCore.Qt.AlignVCenter)
self.label_11.setObjectName(_fromUtf8("label_11"))
self.gridLayout_6.addWidget(self.label_11, 3, 1, 1, 1)
self.label_8 = QtGui.QLabel(self.tabDemandedDifficulty)
@@ -285,7 +300,7 @@ class Ui_settingsDialog(object):
self.gridLayout_7.addItem(spacerItem6, 1, 0, 1, 1)
self.label_13 = QtGui.QLabel(self.tabMaxAcceptableDifficulty)
self.label_13.setLayoutDirection(QtCore.Qt.LeftToRight)
- self.label_13.setAlignment(QtCore.Qt.AlignRight|QtCore.Qt.AlignTrailing|QtCore.Qt.AlignVCenter)
+ self.label_13.setAlignment(QtCore.Qt.AlignRight | QtCore.Qt.AlignTrailing | QtCore.Qt.AlignVCenter)
self.label_13.setObjectName(_fromUtf8("label_13"))
self.gridLayout_7.addWidget(self.label_13, 1, 1, 1, 1)
self.lineEditMaxAcceptableTotalDifficulty = QtGui.QLineEdit(self.tabMaxAcceptableDifficulty)
@@ -300,7 +315,7 @@ class Ui_settingsDialog(object):
spacerItem7 = QtGui.QSpacerItem(102, 20, QtGui.QSizePolicy.Expanding, QtGui.QSizePolicy.Minimum)
self.gridLayout_7.addItem(spacerItem7, 2, 0, 1, 1)
self.label_14 = QtGui.QLabel(self.tabMaxAcceptableDifficulty)
- self.label_14.setAlignment(QtCore.Qt.AlignRight|QtCore.Qt.AlignTrailing|QtCore.Qt.AlignVCenter)
+ self.label_14.setAlignment(QtCore.Qt.AlignRight | QtCore.Qt.AlignTrailing | QtCore.Qt.AlignVCenter)
self.label_14.setObjectName(_fromUtf8("label_14"))
self.gridLayout_7.addWidget(self.label_14, 2, 1, 1, 1)
self.lineEditMaxAcceptableSmallMessageDifficulty = QtGui.QLineEdit(self.tabMaxAcceptableDifficulty)
@@ -310,7 +325,8 @@ class Ui_settingsDialog(object):
sizePolicy.setHeightForWidth(self.lineEditMaxAcceptableSmallMessageDifficulty.sizePolicy().hasHeightForWidth())
self.lineEditMaxAcceptableSmallMessageDifficulty.setSizePolicy(sizePolicy)
self.lineEditMaxAcceptableSmallMessageDifficulty.setMaximumSize(QtCore.QSize(70, 16777215))
- self.lineEditMaxAcceptableSmallMessageDifficulty.setObjectName(_fromUtf8("lineEditMaxAcceptableSmallMessageDifficulty"))
+ self.lineEditMaxAcceptableSmallMessageDifficulty.setObjectName(
+ _fromUtf8("lineEditMaxAcceptableSmallMessageDifficulty"))
self.gridLayout_7.addWidget(self.lineEditMaxAcceptableSmallMessageDifficulty, 2, 2, 1, 1)
spacerItem8 = QtGui.QSpacerItem(20, 147, QtGui.QSizePolicy.Minimum, QtGui.QSizePolicy.Expanding)
self.gridLayout_7.addItem(spacerItem8, 3, 1, 1, 1)
@@ -332,7 +348,7 @@ class Ui_settingsDialog(object):
self.label_16.setObjectName(_fromUtf8("label_16"))
self.gridLayout_8.addWidget(self.label_16, 0, 0, 1, 3)
self.label_17 = QtGui.QLabel(self.tabNamecoin)
- self.label_17.setAlignment(QtCore.Qt.AlignRight|QtCore.Qt.AlignTrailing|QtCore.Qt.AlignVCenter)
+ self.label_17.setAlignment(QtCore.Qt.AlignRight | QtCore.Qt.AlignTrailing | QtCore.Qt.AlignVCenter)
self.label_17.setObjectName(_fromUtf8("label_17"))
self.gridLayout_8.addWidget(self.label_17, 2, 1, 1, 1)
self.lineEditNamecoinHost = QtGui.QLineEdit(self.tabNamecoin)
@@ -344,7 +360,7 @@ class Ui_settingsDialog(object):
self.gridLayout_8.addItem(spacerItem11, 4, 0, 1, 1)
self.label_18 = QtGui.QLabel(self.tabNamecoin)
self.label_18.setEnabled(True)
- self.label_18.setAlignment(QtCore.Qt.AlignRight|QtCore.Qt.AlignTrailing|QtCore.Qt.AlignVCenter)
+ self.label_18.setAlignment(QtCore.Qt.AlignRight | QtCore.Qt.AlignTrailing | QtCore.Qt.AlignVCenter)
self.label_18.setObjectName(_fromUtf8("label_18"))
self.gridLayout_8.addWidget(self.label_18, 3, 1, 1, 1)
self.lineEditNamecoinPort = QtGui.QLineEdit(self.tabNamecoin)
@@ -353,7 +369,7 @@ class Ui_settingsDialog(object):
spacerItem12 = QtGui.QSpacerItem(20, 40, QtGui.QSizePolicy.Minimum, QtGui.QSizePolicy.Expanding)
self.gridLayout_8.addItem(spacerItem12, 8, 1, 1, 1)
self.labelNamecoinUser = QtGui.QLabel(self.tabNamecoin)
- self.labelNamecoinUser.setAlignment(QtCore.Qt.AlignRight|QtCore.Qt.AlignTrailing|QtCore.Qt.AlignVCenter)
+ self.labelNamecoinUser.setAlignment(QtCore.Qt.AlignRight | QtCore.Qt.AlignTrailing | QtCore.Qt.AlignVCenter)
self.labelNamecoinUser.setObjectName(_fromUtf8("labelNamecoinUser"))
self.gridLayout_8.addWidget(self.labelNamecoinUser, 4, 1, 1, 1)
self.lineEditNamecoinUser = QtGui.QLineEdit(self.tabNamecoin)
@@ -362,11 +378,13 @@ class Ui_settingsDialog(object):
spacerItem13 = QtGui.QSpacerItem(40, 20, QtGui.QSizePolicy.Expanding, QtGui.QSizePolicy.Minimum)
self.gridLayout_8.addItem(spacerItem13, 5, 0, 1, 1)
self.labelNamecoinPassword = QtGui.QLabel(self.tabNamecoin)
- self.labelNamecoinPassword.setAlignment(QtCore.Qt.AlignRight|QtCore.Qt.AlignTrailing|QtCore.Qt.AlignVCenter)
+ self.labelNamecoinPassword.setAlignment(
+ QtCore.Qt.AlignRight | QtCore.Qt.AlignTrailing | QtCore.Qt.AlignVCenter)
self.labelNamecoinPassword.setObjectName(_fromUtf8("labelNamecoinPassword"))
self.gridLayout_8.addWidget(self.labelNamecoinPassword, 5, 1, 1, 1)
self.lineEditNamecoinPassword = QtGui.QLineEdit(self.tabNamecoin)
- self.lineEditNamecoinPassword.setInputMethodHints(QtCore.Qt.ImhHiddenText|QtCore.Qt.ImhNoAutoUppercase|QtCore.Qt.ImhNoPredictiveText)
+ self.lineEditNamecoinPassword.setInputMethodHints(
+ QtCore.Qt.ImhHiddenText | QtCore.Qt.ImhNoAutoUppercase | QtCore.Qt.ImhNoPredictiveText)
self.lineEditNamecoinPassword.setEchoMode(QtGui.QLineEdit.Password)
self.lineEditNamecoinPassword.setObjectName(_fromUtf8("lineEditNamecoinPassword"))
self.gridLayout_8.addWidget(self.lineEditNamecoinPassword, 5, 2, 1, 1)
@@ -405,11 +423,11 @@ class Ui_settingsDialog(object):
self.widget.setObjectName(_fromUtf8("widget"))
self.label_19 = QtGui.QLabel(self.widget)
self.label_19.setGeometry(QtCore.QRect(10, 20, 101, 20))
- self.label_19.setAlignment(QtCore.Qt.AlignRight|QtCore.Qt.AlignTrailing|QtCore.Qt.AlignVCenter)
+ self.label_19.setAlignment(QtCore.Qt.AlignRight | QtCore.Qt.AlignTrailing | QtCore.Qt.AlignVCenter)
self.label_19.setObjectName(_fromUtf8("label_19"))
self.label_20 = QtGui.QLabel(self.widget)
self.label_20.setGeometry(QtCore.QRect(30, 40, 80, 16))
- self.label_20.setAlignment(QtCore.Qt.AlignRight|QtCore.Qt.AlignTrailing|QtCore.Qt.AlignVCenter)
+ self.label_20.setAlignment(QtCore.Qt.AlignRight | QtCore.Qt.AlignTrailing | QtCore.Qt.AlignVCenter)
self.label_20.setObjectName(_fromUtf8("label_20"))
self.lineEditDays = QtGui.QLineEdit(self.widget)
self.lineEditDays.setGeometry(QtCore.QRect(113, 20, 51, 20))
@@ -431,10 +449,20 @@ class Ui_settingsDialog(object):
self.retranslateUi(settingsDialog)
self.tabWidgetSettings.setCurrentIndex(0)
- QtCore.QObject.connect(self.buttonBox, QtCore.SIGNAL(_fromUtf8("accepted()")), settingsDialog.accept)
- QtCore.QObject.connect(self.buttonBox, QtCore.SIGNAL(_fromUtf8("rejected()")), settingsDialog.reject)
- QtCore.QObject.connect(self.checkBoxAuthentication, QtCore.SIGNAL(_fromUtf8("toggled(bool)")), self.lineEditSocksUsername.setEnabled)
- QtCore.QObject.connect(self.checkBoxAuthentication, QtCore.SIGNAL(_fromUtf8("toggled(bool)")), self.lineEditSocksPassword.setEnabled)
+ QtCore.QObject.connect( # pylint: disable=no-member
+ self.buttonBox, QtCore.SIGNAL(_fromUtf8("accepted()")), settingsDialog.accept)
+ QtCore.QObject.connect( # pylint: disable=no-member
+ self.buttonBox, QtCore.SIGNAL(_fromUtf8("rejected()")), settingsDialog.reject)
+ QtCore.QObject.connect( # pylint: disable=no-member
+ self.checkBoxAuthentication,
+ QtCore.SIGNAL(
+ _fromUtf8("toggled(bool)")),
+ self.lineEditSocksUsername.setEnabled)
+ QtCore.QObject.connect( # pylint: disable=no-member
+ self.checkBoxAuthentication,
+ QtCore.SIGNAL(
+ _fromUtf8("toggled(bool)")),
+ self.lineEditSocksPassword.setEnabled)
QtCore.QMetaObject.connectSlotsByName(settingsDialog)
settingsDialog.setTabOrder(self.tabWidgetSettings, self.checkBoxStartOnLogon)
settingsDialog.setTabOrder(self.checkBoxStartOnLogon, self.checkBoxStartInTray)
@@ -450,22 +478,47 @@ class Ui_settingsDialog(object):
settingsDialog.setTabOrder(self.checkBoxSocksListen, self.buttonBox)
def retranslateUi(self, settingsDialog):
+ """Re-translate the UI into the supported languages"""
+
settingsDialog.setWindowTitle(_translate("settingsDialog", "Settings", None))
self.checkBoxStartOnLogon.setText(_translate("settingsDialog", "Start Bitmessage on user login", None))
self.groupBoxTray.setTitle(_translate("settingsDialog", "Tray", None))
- self.checkBoxStartInTray.setText(_translate("settingsDialog", "Start Bitmessage in the tray (don\'t show main window)", None))
+ self.checkBoxStartInTray.setText(
+ _translate(
+ "settingsDialog",
+ "Start Bitmessage in the tray (don\'t show main window)",
+ None))
self.checkBoxMinimizeToTray.setText(_translate("settingsDialog", "Minimize to tray", None))
self.checkBoxTrayOnClose.setText(_translate("settingsDialog", "Close to tray", None))
- self.checkBoxHideTrayConnectionNotifications.setText(_translate("settingsDialog", "Hide connection notifications", None))
- self.checkBoxShowTrayNotifications.setText(_translate("settingsDialog", "Show notification when message received", None))
+ self.checkBoxHideTrayConnectionNotifications.setText(
+ _translate("settingsDialog", "Hide connection notifications", None))
+ self.checkBoxShowTrayNotifications.setText(
+ _translate(
+ "settingsDialog",
+ "Show notification when message received",
+ None))
self.checkBoxPortableMode.setText(_translate("settingsDialog", "Run in Portable Mode", None))
- self.PortableModeDescription.setText(_translate("settingsDialog", "In Portable Mode, messages and config files are stored in the same directory as the program rather than the normal application-data folder. This makes it convenient to run Bitmessage from a USB thumb drive.", None))
- self.checkBoxWillinglySendToMobile.setText(_translate("settingsDialog", "Willingly include unencrypted destination address when sending to a mobile device", None))
+ self.PortableModeDescription.setText(
+ _translate(
+ "settingsDialog",
+ "In Portable Mode, messages and config files are stored in the same directory as the"
+ " program rather than the normal application-data folder. This makes it convenient to"
+ " run Bitmessage from a USB thumb drive.",
+ None))
+ self.checkBoxWillinglySendToMobile.setText(
+ _translate(
+ "settingsDialog",
+ "Willingly include unencrypted destination address when sending to a mobile device",
+ None))
self.checkBoxUseIdenticons.setText(_translate("settingsDialog", "Use Identicons", None))
self.checkBoxReplyBelow.setText(_translate("settingsDialog", "Reply below Quote", None))
self.groupBox.setTitle(_translate("settingsDialog", "Interface Language", None))
self.languageComboBox.setItemText(0, _translate("settingsDialog", "System Settings", "system"))
- self.tabWidgetSettings.setTabText(self.tabWidgetSettings.indexOf(self.tabUserInterface), _translate("settingsDialog", "User Interface", None))
+ self.tabWidgetSettings.setTabText(
+ self.tabWidgetSettings.indexOf(
+ self.tabUserInterface),
+ _translate(
+ "settingsDialog", "User Interface", None))
self.groupBox1.setTitle(_translate("settingsDialog", "Listening port", None))
self.label.setText(_translate("settingsDialog", "Listen for connections on port:", None))
self.labelUPnP.setText(_translate("settingsDialog", "UPnP:", None))
@@ -480,23 +533,70 @@ class Ui_settingsDialog(object):
self.checkBoxAuthentication.setText(_translate("settingsDialog", "Authentication", None))
self.label_5.setText(_translate("settingsDialog", "Username:", None))
self.label_6.setText(_translate("settingsDialog", "Pass:", None))
- self.checkBoxSocksListen.setText(_translate("settingsDialog", "Listen for incoming connections when using proxy", None))
+ self.checkBoxSocksListen.setText(
+ _translate(
+ "settingsDialog",
+ "Listen for incoming connections when using proxy",
+ None))
self.comboBoxProxyType.setItemText(0, _translate("settingsDialog", "none", None))
self.comboBoxProxyType.setItemText(1, _translate("settingsDialog", "SOCKS4a", None))
self.comboBoxProxyType.setItemText(2, _translate("settingsDialog", "SOCKS5", None))
- self.tabWidgetSettings.setTabText(self.tabWidgetSettings.indexOf(self.tabNetworkSettings), _translate("settingsDialog", "Network Settings", None))
+ self.tabWidgetSettings.setTabText(
+ self.tabWidgetSettings.indexOf(
+ self.tabNetworkSettings),
+ _translate(
+ "settingsDialog", "Network Settings", None))
self.label_9.setText(_translate("settingsDialog", "Total difficulty:", None))
- self.label_10.setText(_translate("settingsDialog", "The \'Total difficulty\' affects the absolute amount of work the sender must complete. Doubling this value doubles the amount of work.", None))
+ self.label_10.setText(
+ _translate(
+ "settingsDialog",
+ "The \'Total difficulty\' affects the absolute amount of work the sender must complete."
+ " Doubling this value doubles the amount of work.",
+ None))
self.label_11.setText(_translate("settingsDialog", "Small message difficulty:", None))
- self.label_8.setText(_translate("settingsDialog", "When someone sends you a message, their computer must first complete some work. The difficulty of this work, by default, is 1. You may raise this default for new addresses you create by changing the values here. Any new addresses you create will require senders to meet the higher difficulty. There is one exception: if you add a friend or acquaintance to your address book, Bitmessage will automatically notify them when you next send a message that they need only complete the minimum amount of work: difficulty 1. ", None))
- self.label_12.setText(_translate("settingsDialog", "The \'Small message difficulty\' mostly only affects the difficulty of sending small messages. Doubling this value makes it almost twice as difficult to send a small message but doesn\'t really affect large messages.", None))
- self.tabWidgetSettings.setTabText(self.tabWidgetSettings.indexOf(self.tabDemandedDifficulty), _translate("settingsDialog", "Demanded difficulty", None))
- self.label_15.setText(_translate("settingsDialog", "Here you may set the maximum amount of work you are willing to do to send a message to another person. Setting these values to 0 means that any value is acceptable.", None))
+ self.label_8.setText(_translate(
+ "settingsDialog",
+ "When someone sends you a message, their computer must first complete some work. The difficulty of this"
+ " work, by default, is 1. You may raise this default for new addresses you create by changing the values"
+ " here. Any new addresses you create will require senders to meet the higher difficulty. There is one"
+ " exception: if you add a friend or acquaintance to your address book, Bitmessage will automatically"
+ " notify them when you next send a message that they need only complete the minimum amount of"
+ " work: difficulty 1. ",
+ None))
+ self.label_12.setText(
+ _translate(
+ "settingsDialog",
+ "The \'Small message difficulty\' mostly only affects the difficulty of sending small messages."
+ " Doubling this value makes it almost twice as difficult to send a small message but doesn\'t really"
+ " affect large messages.",
+ None))
+ self.tabWidgetSettings.setTabText(
+ self.tabWidgetSettings.indexOf(
+ self.tabDemandedDifficulty),
+ _translate(
+ "settingsDialog", "Demanded difficulty", None))
+ self.label_15.setText(
+ _translate(
+ "settingsDialog",
+ "Here you may set the maximum amount of work you are willing to do to send a message to another"
+ " person. Setting these values to 0 means that any value is acceptable.",
+ None))
self.label_13.setText(_translate("settingsDialog", "Maximum acceptable total difficulty:", None))
self.label_14.setText(_translate("settingsDialog", "Maximum acceptable small message difficulty:", None))
- self.tabWidgetSettings.setTabText(self.tabWidgetSettings.indexOf(self.tabMaxAcceptableDifficulty), _translate("settingsDialog", "Max acceptable difficulty", None))
+ self.tabWidgetSettings.setTabText(
+ self.tabWidgetSettings.indexOf(
+ self.tabMaxAcceptableDifficulty),
+ _translate(
+ "settingsDialog", "Max acceptable difficulty", None))
self.labelOpenCL.setText(_translate("settingsDialog", "Hardware GPU acceleration (OpenCL):", None))
- self.label_16.setText(_translate("settingsDialog", " | Bitmessage can utilize a different Bitcoin-based program called Namecoin to make addresses human-friendly. For example, instead of having to tell your friend your long Bitmessage address, you can simply tell him to send a message to test.
(Getting your own Bitmessage address into Namecoin is still rather difficult).
Bitmessage can use either namecoind directly or a running nmcontrol instance.
", None))
+ self.label_16.setText(_translate(
+ "settingsDialog",
+ "
Bitmessage can utilize a different Bitcoin-based program called Namecoin to make"
+ " addresses human-friendly. For example, instead of having to tell your friend your long Bitmessage"
+ " address, you can simply tell him to send a message to test."
+ "
(Getting your own Bitmessage address into Namecoin is still rather difficult).
"
+ "Bitmessage can use either namecoind directly or a running nmcontrol instance.
",
+ None))
self.label_17.setText(_translate("settingsDialog", "Host:", None))
self.label_18.setText(_translate("settingsDialog", "Port:", None))
self.labelNamecoinUser.setText(_translate("settingsDialog", "Username:", None))
@@ -505,12 +605,26 @@ class Ui_settingsDialog(object):
self.label_21.setText(_translate("settingsDialog", "Connect to:", None))
self.radioButtonNamecoinNamecoind.setText(_translate("settingsDialog", "Namecoind", None))
self.radioButtonNamecoinNmcontrol.setText(_translate("settingsDialog", "NMControl", None))
- self.tabWidgetSettings.setTabText(self.tabWidgetSettings.indexOf(self.tabNamecoin), _translate("settingsDialog", "Namecoin integration", None))
- self.label_7.setText(_translate("settingsDialog", "
By default, if you send a message to someone and he is offline for more than two days, Bitmessage will send the message again after an additional two days. This will be continued with exponential backoff forever; messages will be resent after 5, 10, 20 days ect. until the receiver acknowledges them. Here you may change that behavior by having Bitmessage give up after a certain number of days or months.
Leave these input fields blank for the default behavior.
", None))
+ self.tabWidgetSettings.setTabText(
+ self.tabWidgetSettings.indexOf(
+ self.tabNamecoin),
+ _translate(
+ "settingsDialog", "Namecoin integration", None))
+ self.label_7.setText(_translate(
+ "settingsDialog",
+ "
By default, if you send a message to someone and he is offline for more than two"
+ " days, Bitmessage will send the message again after an additional two days. This will be continued with"
+ " exponential backoff forever; messages will be resent after 5, 10, 20 days ect. until the receiver"
+ " acknowledges them. Here you may change that behavior by having Bitmessage give up after a certain"
+ " number of days or months.
Leave these input fields blank for the default behavior."
+ "
",
+ None))
self.label_19.setText(_translate("settingsDialog", "Give up after", None))
self.label_20.setText(_translate("settingsDialog", "and", None))
self.label_22.setText(_translate("settingsDialog", "days", None))
self.label_23.setText(_translate("settingsDialog", "months.", None))
- self.tabWidgetSettings.setTabText(self.tabWidgetSettings.indexOf(self.tabResendsExpire), _translate("settingsDialog", "Resends Expire", None))
-
-import bitmessage_icons_rc
+ self.tabWidgetSettings.setTabText(
+ self.tabWidgetSettings.indexOf(
+ self.tabResendsExpire),
+ _translate(
+ "settingsDialog", "Resends Expire", None))
diff --git a/src/namecoin.py b/src/namecoin.py
index 9b3c3c3e..7f081bc6 100644
--- a/src/namecoin.py
+++ b/src/namecoin.py
@@ -1,54 +1,64 @@
-# Copyright (C) 2013 by Daniel Kraft
-# This file is part of the Bitmessage project.
-#
-# Permission is hereby granted, free of charge, to any person obtaining a copy
-# of this software and associated documentation files (the "Software"), to deal
-# in the Software without restriction, including without limitation the rights
-# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
-# copies of the Software, and to permit persons to whom the Software is
-# furnished to do so, subject to the following conditions:
-#
-# The above copyright notice and this permission notice shall be included in all
-# copies or substantial portions of the Software.
-#
-# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
-# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
-# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
-# SOFTWARE.
+# pylint: disable=too-many-branches,protected-access
+"""
+Copyright (C) 2013 by Daniel Kraft
+This file is part of the Bitmessage project.
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.
+
+.. todo:: from debug import logger crashes PyBitmessage due to a circular dependency. The debug module will also
+override/disable logging.getLogger() # loggers so module level logging functions are used instead
+"""
+
+from __future__ import absolute_import
import base64
import httplib
import json
+import os
import socket
import sys
-import os
-from bmconfigparser import BMConfigParser
-import defaults
-import tr # translate
-
-# FIXME: from debug import logger crashes PyBitmessage due to a circular
-# dependency. The debug module will also override/disable logging.getLogger()
-# loggers so module level logging functions are used instead
import logging as logger
+import defaults
+import tr # translate
+from bmconfigparser import BMConfigParser
+
configSection = "bitmessagesettings"
-# Error thrown when the RPC call returns an error.
-class RPCError (Exception):
+
+class RPCError(Exception):
+ """Error thrown when the RPC call returns an error."""
+
error = None
- def __init__ (self, data):
+ def __init__(self, data):
+ super(RPCError, self).__init__()
self.error = data
-
+
def __str__(self):
return '{0}: {1}'.format(type(self).__name__, self.error)
-# This class handles the Namecoin identity integration.
-class namecoinConnection (object):
+
+class namecoinConnection(object):
+ """This class handles the Namecoin identity integration."""
+
user = None
password = None
host = None
@@ -58,47 +68,51 @@ class namecoinConnection (object):
queryid = 1
con = None
- # 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).
- def __init__ (self, options = None):
+ 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
+ currently entered connection settings in the config dialog without
+ actually changing the values (yet).
+ """
if options is None:
- self.nmctype = BMConfigParser().get (configSection, "namecoinrpctype")
- self.host = BMConfigParser().get (configSection, "namecoinrpchost")
- self.port = int(BMConfigParser().get (configSection, "namecoinrpcport"))
- self.user = BMConfigParser().get (configSection, "namecoinrpcuser")
- self.password = BMConfigParser().get (configSection,
- "namecoinrpcpassword")
+ self.nmctype = BMConfigParser().get(configSection, "namecoinrpctype")
+ self.host = BMConfigParser().get(configSection, "namecoinrpchost")
+ self.port = int(BMConfigParser().get(configSection, "namecoinrpcport"))
+ self.user = BMConfigParser().get(configSection, "namecoinrpcuser")
+ self.password = BMConfigParser().get(configSection,
+ "namecoinrpcpassword")
else:
- self.nmctype = options["type"]
- self.host = options["host"]
- self.port = int(options["port"])
- self.user = options["user"]
- self.password = options["password"]
+ self.nmctype = options["type"]
+ self.host = options["host"]
+ self.port = int(options["port"])
+ self.user = options["user"]
+ self.password = options["password"]
assert self.nmctype == "namecoind" or self.nmctype == "nmcontrol"
if self.nmctype == "namecoind":
- self.con = httplib.HTTPConnection(self.host, self.port, timeout = 3)
+ self.con = httplib.HTTPConnection(self.host, self.port, timeout=3)
- # Query for the bitmessage address corresponding to the given identity
- # 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.
- def query (self, string):
- slashPos = string.find ("/")
+ def query(self, string):
+ """
+ Query for the bitmessage address corresponding to the given identity
+ 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.
+ """
+ slashPos = string.find("/")
if slashPos < 0:
string = "id/" + string
try:
if self.nmctype == "namecoind":
- res = self.callRPC ("name_show", [string])
+ res = self.callRPC("name_show", [string])
res = res["value"]
elif self.nmctype == "nmcontrol":
- res = self.callRPC ("data", ["getValue", string])
+ res = self.callRPC("data", ["getValue", string])
res = res["reply"]
- if res == False:
- return (tr._translate("MainWindow",'The name %1 was not found.').arg(unicode(string)), None)
+ if not res:
+ return (tr._translate("MainWindow", 'The name %1 was not found.').arg(unicode(string)), None)
else:
assert False
except RPCError as exc:
@@ -107,16 +121,16 @@ class namecoinConnection (object):
errmsg = exc.error["message"]
else:
errmsg = exc.error
- return (tr._translate("MainWindow",'The namecoin query failed (%1)').arg(unicode(errmsg)), None)
- except Exception as exc:
+ return (tr._translate("MainWindow", 'The namecoin query failed (%1)').arg(unicode(errmsg)), None)
+ except Exception:
logger.exception("Namecoin query exception")
- return (tr._translate("MainWindow",'The namecoin query failed.'), None)
+ return (tr._translate("MainWindow", 'The namecoin query failed.'), None)
try:
- val = json.loads (res)
+ val = json.loads(res)
except:
logger.exception("Namecoin query json exception")
- return (tr._translate("MainWindow",'The name %1 has no valid JSON data.').arg(unicode(string)), None)
+ return (tr._translate("MainWindow", 'The name %1 has no valid JSON data.').arg(unicode(string)), None)
if "bitmessage" in val:
if "name" in val:
@@ -124,12 +138,19 @@ class namecoinConnection (object):
else:
ret = val["bitmessage"]
return (None, ret)
- return (tr._translate("MainWindow",'The name %1 has no associated Bitmessage address.').arg(unicode(string)), None)
+ return (
+ tr._translate(
+ "MainWindow",
+ 'The name %1 has no associated Bitmessage address.').arg(
+ unicode(string)),
+ None)
- # 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.
def test(self):
+ """
+ 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.
+ """
try:
if self.nmctype == "namecoind":
try:
@@ -143,22 +164,30 @@ class namecoinConnection (object):
vers = vers / 100
v1 = vers
if v3 == 0:
- versStr = "0.%d.%d" % (v1, v2)
+ versStr = "0.%d.%d" % (v1, v2)
else:
- versStr = "0.%d.%d.%d" % (v1, v2, v3)
- return ('success', tr._translate("MainWindow",'Success! Namecoind version %1 running.').arg(unicode(versStr)) )
+ versStr = "0.%d.%d.%d" % (v1, v2, v3)
+ message = (
+ 'success',
+ tr._translate(
+ "MainWindow",
+ 'Success! Namecoind version %1 running.').arg(
+ unicode(versStr)))
elif self.nmctype == "nmcontrol":
- res = self.callRPC ("data", ["status"])
+ res = self.callRPC("data", ["status"])
prefix = "Plugin data running"
if ("reply" in res) and res["reply"][:len(prefix)] == prefix:
- return ('success', tr._translate("MainWindow",'Success! NMControll is up and running.'))
+ return ('success', tr._translate("MainWindow", 'Success! NMControll is up and running.'))
logger.error("Unexpected nmcontrol reply: %s", res)
- return ('failed', tr._translate("MainWindow",'Couldn\'t understand NMControl.'))
+ message = ('failed', tr._translate("MainWindow", 'Couldn\'t understand NMControl.'))
else:
- assert False
+ print "Unsupported Namecoin type"
+ sys.exit(1)
+
+ return message
except Exception:
logger.info("Namecoin connection test failure")
@@ -168,20 +197,21 @@ class namecoinConnection (object):
"MainWindow", "The connection to namecoin failed.")
)
- # Helper routine that actually performs an JSON RPC call.
- def callRPC (self, method, params):
+ def callRPC(self, method, params):
+ """Helper routine that actually performs an JSON RPC call."""
+
data = {"method": method, "params": params, "id": self.queryid}
if self.nmctype == "namecoind":
- resp = self.queryHTTP (json.dumps (data))
+ resp = self.queryHTTP(json.dumps(data))
elif self.nmctype == "nmcontrol":
- resp = self.queryServer (json.dumps (data))
+ resp = self.queryServer(json.dumps(data))
else:
- assert False
- val = json.loads (resp)
+ assert False
+ val = json.loads(resp)
if val["id"] != self.queryid:
- raise Exception ("ID mismatch in JSON RPC answer.")
-
+ raise Exception("ID mismatch in JSON RPC answer.")
+
if self.nmctype == "namecoind":
self.queryid = self.queryid + 1
@@ -190,11 +220,12 @@ class namecoinConnection (object):
return val["result"]
if isinstance(error, bool):
- raise RPCError (val["result"])
- raise RPCError (error)
+ raise RPCError(val["result"])
+ raise RPCError(error)
+
+ def queryHTTP(self, data):
+ """Query the server via HTTP."""
- # Query the server via HTTP.
- def queryHTTP (self, data):
result = None
try:
@@ -206,14 +237,14 @@ class namecoinConnection (object):
self.con.putheader("Content-Length", str(len(data)))
self.con.putheader("Accept", "application/json")
authstr = "%s:%s" % (self.user, self.password)
- self.con.putheader("Authorization", "Basic %s" % base64.b64encode (authstr))
+ self.con.putheader("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)
+ raise Exception("Namecoin returned status %i: %s" % resp.status, resp.reason)
except:
logger.info("HTTP receive error")
except:
@@ -221,41 +252,49 @@ class namecoinConnection (object):
return result
- # Helper routine sending data to the RPC server and returning the result.
- def queryServer (self, data):
+ def queryServer(self, data):
+ """Helper routine sending data to the RPC server and returning the result."""
+
try:
- s = socket.socket (socket.AF_INET, socket.SOCK_STREAM)
- s.setsockopt (socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
- s.settimeout(3)
- s.connect ((self.host, self.port))
- s.sendall (data)
+ s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
+ s.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
+ s.settimeout(3)
+ s.connect((self.host, self.port))
+ s.sendall(data)
result = ""
while True:
- tmp = s.recv (self.bufsize)
+ tmp = s.recv(self.bufsize)
if not tmp:
- break
+ break
result += tmp
- s.close ()
+ s.close()
return result
except socket.error as exc:
- raise Exception ("Socket error in RPC connection: %s" % str (exc))
+ raise Exception("Socket error in RPC connection: %s" % str(exc))
+
+
+def lookupNamecoinFolder():
+ """
+ Look up the namecoin data folder.
+
+ .. todo:: Check whether this works on other platforms as well!
+ """
-# Look up the namecoin data folder.
-# FIXME: Check whether this works on other platforms as well!
-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) + '/'
+ dataFolder = path.join(os.environ["HOME"],
+ "Library/Application Support/", app) + '/'
else:
- print ("Could not find home folder, please report this message"
- + " and your OS X version to the BitMessage Github.")
+ print(
+ "Could not find home folder, please report this message"
+ " and your OS X version to the BitMessage Github."
+ )
sys.exit()
elif "win32" in sys.platform or "win64" in sys.platform:
@@ -265,34 +304,38 @@ def lookupNamecoinFolder ():
return dataFolder
-# Ensure all namecoin options are set, by setting those to default values
-# that aren't there.
-def ensureNamecoinOptions ():
- if not BMConfigParser().has_option (configSection, "namecoinrpctype"):
- BMConfigParser().set (configSection, "namecoinrpctype", "namecoind")
- if not BMConfigParser().has_option (configSection, "namecoinrpchost"):
- BMConfigParser().set (configSection, "namecoinrpchost", "localhost")
- hasUser = BMConfigParser().has_option (configSection, "namecoinrpcuser")
- hasPass = BMConfigParser().has_option (configSection, "namecoinrpcpassword")
- hasPort = BMConfigParser().has_option (configSection, "namecoinrpcport")
+def ensureNamecoinOptions():
+ """
+ Ensure all namecoin options are set, by setting those to default values
+ that aren't there.
+ """
+
+ if not BMConfigParser().has_option(configSection, "namecoinrpctype"):
+ BMConfigParser().set(configSection, "namecoinrpctype", "namecoind")
+ if not BMConfigParser().has_option(configSection, "namecoinrpchost"):
+ BMConfigParser().set(configSection, "namecoinrpchost", "localhost")
+
+ hasUser = BMConfigParser().has_option(configSection, "namecoinrpcuser")
+ hasPass = BMConfigParser().has_option(configSection, "namecoinrpcpassword")
+ hasPort = BMConfigParser().has_option(configSection, "namecoinrpcport")
# Try to read user/password from .namecoin configuration file.
defaultUser = ""
defaultPass = ""
- nmcFolder = lookupNamecoinFolder ()
+ nmcFolder = lookupNamecoinFolder()
nmcConfig = nmcFolder + "namecoin.conf"
try:
- nmc = open (nmcConfig, "r")
+ nmc = open(nmcConfig, "r")
while True:
- line = nmc.readline ()
+ line = nmc.readline()
if line == "":
break
- parts = line.split ("=")
- if len (parts) == 2:
+ parts = line.split("=")
+ if len(parts) == 2:
key = parts[0]
- val = parts[1].rstrip ()
+ val = parts[1].rstrip()
if key == "rpcuser" and not hasUser:
defaultUser = val
@@ -300,20 +343,20 @@ def ensureNamecoinOptions ():
defaultPass = val
if key == "rpcport":
defaults.namecoinDefaultRpcPort = val
-
- nmc.close ()
+
+ nmc.close()
except IOError:
logger.error("%s unreadable or missing, Namecoin support deactivated", nmcConfig)
- except Exception as exc:
+ except Exception:
logger.warning("Error processing namecoin.conf", exc_info=True)
# If still nothing found, set empty at least.
- if (not hasUser):
- BMConfigParser().set (configSection, "namecoinrpcuser", defaultUser)
- if (not hasPass):
- BMConfigParser().set (configSection, "namecoinrpcpassword", defaultPass)
+ if not hasUser:
+ BMConfigParser().set(configSection, "namecoinrpcuser", defaultUser)
+ if not hasPass:
+ BMConfigParser().set(configSection, "namecoinrpcpassword", defaultPass)
# Set default port now, possibly to found value.
- if (not hasPort):
- BMConfigParser().set (configSection, "namecoinrpcport",
- defaults.namecoinDefaultRpcPort)
+ if not hasPort:
+ BMConfigParser().set(configSection, "namecoinrpcport",
+ defaults.namecoinDefaultRpcPort)
diff --git a/src/network/asyncore_pollchoose.py b/src/network/asyncore_pollchoose.py
index e50be61b..233a49b1 100644
--- a/src/network/asyncore_pollchoose.py
+++ b/src/network/asyncore_pollchoose.py
@@ -1,7 +1,9 @@
# -*- Mode: Python -*-
# Id: asyncore.py,v 2.51 2000/09/07 22:29:26 rushing Exp
# Author: Sam Rushing
-
+# pylint: disable=too-many-statements,too-many-branches,no-self-use,too-many-lines,attribute-defined-outside-init
+# pylint: disable=global-statement
+"""
# ======================================================================
# Copyright 1996 by Sam Rushing
#
@@ -25,7 +27,7 @@
# CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
# ======================================================================
-"""Basic infrastructure for asynchronous socket service clients and servers.
+Basic infrastructure for asynchronous socket service clients and servers.
There are only two ways to have a program on a single processor do "more
than one thing at a time". Multi-threaded programming is the simplest and
@@ -46,22 +48,18 @@ many of the difficult problems for you, making the task of building
sophisticated high-performance network servers and clients a snap.
"""
-# randomise object order for bandwidth balancing
-import random
+import os
import select
import socket
import sys
import time
from threading import current_thread
import warnings
-
-import os
-import helper_random
from errno import EALREADY, EINPROGRESS, EWOULDBLOCK, ECONNRESET, EINVAL, \
- ENOTCONN, ESHUTDOWN, EISCONN, EBADF, ECONNABORTED, EPIPE, EAGAIN, \
- ECONNREFUSED, EHOSTUNREACH, ENETUNREACH, ENOTSOCK, EINTR, ETIMEDOUT, \
- EADDRINUSE, \
- errorcode
+ ENOTCONN, ESHUTDOWN, EISCONN, EBADF, ECONNABORTED, EPIPE, EAGAIN, \
+ ECONNREFUSED, EHOSTUNREACH, ENETUNREACH, ENOTSOCK, EINTR, ETIMEDOUT, \
+ EADDRINUSE, \
+ errorcode
try:
from errno import WSAEWOULDBLOCK
except (ImportError, AttributeError):
@@ -75,13 +73,16 @@ try:
except (ImportError, AttributeError):
WSAECONNRESET = ECONNRESET
try:
- from errno import WSAEADDRINUSE
+ # side-effects on Windows or cruft?
+ from errno import WSAEADDRINUSE # pylint: disable=unused-import
except (ImportError, AttributeError):
WSAEADDRINUSE = EADDRINUSE
+import helper_random
-_DISCONNECTED = frozenset((ECONNRESET, ENOTCONN, ESHUTDOWN, ECONNABORTED, EPIPE,
- EBADF, ECONNREFUSED, EHOSTUNREACH, ENETUNREACH, ETIMEDOUT,
- WSAECONNRESET))
+
+_DISCONNECTED = frozenset((
+ ECONNRESET, ENOTCONN, ESHUTDOWN, ECONNABORTED, EPIPE, EBADF, ECONNREFUSED,
+ EHOSTUNREACH, ENETUNREACH, ETIMEDOUT, WSAECONNRESET))
OP_READ = 1
OP_WRITE = 2
@@ -91,17 +92,21 @@ try:
except NameError:
socket_map = {}
+
def _strerror(err):
try:
return os.strerror(err)
except (ValueError, OverflowError, NameError):
if err in errorcode:
return errorcode[err]
- return "Unknown error %s" %err
+ return "Unknown error %s" % err
+
class ExitNow(Exception):
+ """We don't use directly but may be necessary as we replace asyncore due to some library raising or expecting it"""
pass
+
_reraised_exceptions = (ExitNow, KeyboardInterrupt, SystemExit)
maxDownloadRate = 0
@@ -113,7 +118,10 @@ uploadTimestamp = 0
uploadBucket = 0
sentBytes = 0
+
def read(obj):
+ """Read an event from the object"""
+
if not can_receive():
return
try:
@@ -123,7 +131,10 @@ def read(obj):
except:
obj.handle_error()
+
def write(obj):
+ """Write an event to the object"""
+
if not can_send():
return
try:
@@ -133,8 +144,12 @@ def write(obj):
except:
obj.handle_error()
+
def set_rates(download, upload):
+ """Set throttling rates"""
+
global maxDownloadRate, maxUploadRate, downloadBucket, uploadBucket, downloadTimestamp, uploadTimestamp
+
maxDownloadRate = float(download) * 1024
maxUploadRate = float(upload) * 1024
downloadBucket = maxDownloadRate
@@ -142,14 +157,24 @@ def set_rates(download, upload):
downloadTimestamp = time.time()
uploadTimestamp = time.time()
+
def can_receive():
+ """Predicate indicating whether the download throttle is in effect"""
+
return maxDownloadRate == 0 or downloadBucket > 0
+
def can_send():
+ """Predicate indicating whether the upload throttle is in effect"""
+
return maxUploadRate == 0 or uploadBucket > 0
+
def update_received(download=0):
+ """Update the receiving throttle"""
+
global receivedBytes, downloadBucket, downloadTimestamp
+
currentTimestamp = time.time()
receivedBytes += download
if maxDownloadRate > 0:
@@ -160,8 +185,12 @@ def update_received(download=0):
downloadBucket -= download
downloadTimestamp = currentTimestamp
+
def update_sent(upload=0):
+ """Update the sending throttle"""
+
global sentBytes, uploadBucket, uploadTimestamp
+
currentTimestamp = time.time()
sentBytes += upload
if maxUploadRate > 0:
@@ -172,7 +201,10 @@ def update_sent(upload=0):
uploadBucket -= upload
uploadTimestamp = currentTimestamp
+
def _exception(obj):
+ """Handle exceptions as appropriate"""
+
try:
obj.handle_expt_event()
except _reraised_exceptions:
@@ -180,7 +212,10 @@ def _exception(obj):
except:
obj.handle_error()
+
def readwrite(obj, flags):
+ """Read and write any pending data to/from the object"""
+
try:
if flags & select.POLLIN and can_receive():
obj.handle_read_event()
@@ -200,12 +235,17 @@ def readwrite(obj, flags):
except:
obj.handle_error()
+
def select_poller(timeout=0.0, map=None):
"""A poller which uses select(), available on most platforms."""
+ # pylint: disable=redefined-builtin
+
if map is None:
map = socket_map
if map:
- r = []; w = []; e = []
+ r = []
+ w = []
+ e = []
for fd, obj in list(map.items()):
is_r = obj.readable()
is_w = obj.writable()
@@ -251,13 +291,16 @@ def select_poller(timeout=0.0, map=None):
else:
current_thread().stop.wait(timeout)
+
def poll_poller(timeout=0.0, map=None):
"""A poller which uses poll(), available on most UNIXen."""
+ # pylint: disable=redefined-builtin
+
if map is None:
map = socket_map
if timeout is not None:
# timeout is in milliseconds
- timeout = int(timeout*1000)
+ timeout = int(timeout * 1000)
try:
poll_poller.pollster
except AttributeError:
@@ -301,12 +344,16 @@ def poll_poller(timeout=0.0, map=None):
else:
current_thread().stop.wait(timeout)
+
# Aliases for backward compatibility
poll = select_poller
poll2 = poll3 = poll_poller
+
def epoll_poller(timeout=0.0, map=None):
"""A poller which uses epoll(), supported on Linux 2.5.44 and newer."""
+ # pylint: disable=redefined-builtin
+
if map is None:
map = socket_map
try:
@@ -346,7 +393,7 @@ def epoll_poller(timeout=0.0, map=None):
if e.errno != EINTR:
raise
r = []
- except select.error, err:
+ except select.error as err:
if err.args[0] != EINTR:
raise
r = []
@@ -354,12 +401,15 @@ def epoll_poller(timeout=0.0, map=None):
obj = map.get(fd)
if obj is None:
continue
- readwrite(obj, flags)
+ readwrite(obj, flags)
else:
current_thread().stop.wait(timeout)
+
def kqueue_poller(timeout=0.0, map=None):
"""A poller which uses kqueue(), BSD specific."""
+ # pylint: disable=redefined-builtin,no-member
+
if map is None:
map = socket_map
try:
@@ -408,7 +458,7 @@ def kqueue_poller(timeout=0.0, map=None):
for event in events:
fd = event.ident
- obj = map.get(fd)
+ obj = map.get(fd)
if obj is None:
continue
if event.flags & select.KQ_EV_ERROR:
@@ -425,13 +475,15 @@ def kqueue_poller(timeout=0.0, map=None):
current_thread().stop.wait(timeout)
-def loop(timeout=30.0, use_poll=False, map=None, count=None,
- poller=None):
+def loop(timeout=30.0, use_poll=False, map=None, count=None, poller=None):
+ """Poll in a loop, forever if count is None"""
+ # pylint: disable=redefined-builtin
+
if map is None:
map = socket_map
if count is None:
- count = True
- # code which grants backward compatibility with "use_poll"
+ count = True
+ # code which grants backward compatibility with "use_poll"
# argument which should no longer be used in favor of
# "poller"
@@ -460,10 +512,13 @@ def loop(timeout=30.0, use_poll=False, map=None, count=None,
break
# then poll
poller(subtimeout, map)
- if type(count) is int:
+ if isinstance(count, int):
count = count - 1
+
class dispatcher:
+ """Dispatcher for socket objects"""
+ # pylint: disable=too-many-public-methods,too-many-instance-attributes,old-style-class
debug = False
connected = False
@@ -478,6 +533,7 @@ class dispatcher:
minTx = 1500
def __init__(self, sock=None, map=None):
+ # pylint: disable=redefined-builtin
if map is None:
self._map = socket_map
else:
@@ -510,7 +566,7 @@ class dispatcher:
self.socket = None
def __repr__(self):
- status = [self.__class__.__module__+"."+self.__class__.__name__]
+ status = [self.__class__.__module__ + "." + self.__class__.__name__]
if self.accepting and self.addr:
status.append('listening')
elif self.connected:
@@ -525,7 +581,9 @@ class dispatcher:
__str__ = __repr__
def add_channel(self, map=None):
- #self.log_info('adding channel %s' % self)
+ """Add a channel"""
+ # pylint: disable=redefined-builtin
+
if map is None:
map = self._map
map[self._fileno] = self
@@ -533,11 +591,13 @@ class dispatcher:
self.poller_filter = 0
def del_channel(self, map=None):
+ """Delete a channel"""
+ # pylint: disable=redefined-builtin
+
fd = self._fileno
if map is None:
map = self._map
if fd in map:
- #self.log_info('closing channel %d:%s' % (fd, self))
del map[fd]
if self._fileno:
try:
@@ -564,25 +624,29 @@ class dispatcher:
self.poller_registered = False
def create_socket(self, family=socket.AF_INET, socket_type=socket.SOCK_STREAM):
+ """Create a socket"""
self.family_and_type = family, socket_type
sock = socket.socket(family, socket_type)
sock.setblocking(0)
self.set_socket(sock)
def set_socket(self, sock, map=None):
+ """Set socket"""
+ # pylint: disable=redefined-builtin
+
self.socket = sock
-## self.__dict__['socket'] = sock
self._fileno = sock.fileno()
self.add_channel(map)
def set_reuse_addr(self):
- # try to re-use a server port if possible
+ """try to re-use a server port if possible"""
+
try:
self.socket.setsockopt(
socket.SOL_SOCKET, socket.SO_REUSEADDR,
self.socket.getsockopt(socket.SOL_SOCKET,
socket.SO_REUSEADDR) | 1
- )
+ )
except socket.error:
pass
@@ -593,11 +657,13 @@ class dispatcher:
# ==================================================
def readable(self):
+ """Predicate to indicate download throttle status"""
if maxDownloadRate > 0:
return downloadBucket > dispatcher.minTx
return True
def writable(self):
+ """Predicate to indicate upload throttle status"""
if maxUploadRate > 0:
return uploadBucket > dispatcher.minTx
return True
@@ -607,21 +673,24 @@ class dispatcher:
# ==================================================
def listen(self, num):
+ """Listen on a port"""
self.accepting = True
if os.name == 'nt' and num > 5:
num = 5
return self.socket.listen(num)
def bind(self, addr):
+ """Bind to an address"""
self.addr = addr
return self.socket.bind(addr)
def connect(self, address):
+ """Connect to an address"""
self.connected = False
self.connecting = True
err = self.socket.connect_ex(address)
if err in (EINPROGRESS, EALREADY, EWOULDBLOCK, WSAEWOULDBLOCK) \
- or err == EINVAL and os.name in ('nt', 'ce'):
+ or err == EINVAL and os.name in ('nt', 'ce'):
self.addr = address
return
if err in (0, EISCONN):
@@ -631,7 +700,11 @@ class dispatcher:
raise socket.error(err, errorcode[err])
def accept(self):
- # XXX can return either an address pair or None
+ """
+ Accept incoming connections
+
+ .. todo:: FIXME: can return either an address pair or None
+ """
try:
conn, addr = self.socket.accept()
except TypeError:
@@ -645,6 +718,7 @@ class dispatcher:
return conn, addr
def send(self, data):
+ """Send data"""
try:
result = self.socket.send(data)
return result
@@ -658,6 +732,7 @@ class dispatcher:
raise
def recv(self, buffer_size):
+ """Receive data"""
try:
data = self.socket.recv(buffer_size)
if not data:
@@ -665,8 +740,7 @@ class dispatcher:
# a read condition, and having recv() return 0.
self.handle_close()
return b''
- else:
- return data
+ return data
except socket.error as why:
# winsock sometimes raises ENOTCONN
if why.args[0] in (EAGAIN, EWOULDBLOCK, WSAEWOULDBLOCK):
@@ -678,6 +752,7 @@ class dispatcher:
raise
def close(self):
+ """Close connection"""
self.connected = False
self.accepting = False
self.connecting = False
@@ -695,10 +770,10 @@ class dispatcher:
retattr = getattr(self.socket, attr)
except AttributeError:
raise AttributeError("%s instance has no attribute '%s'"
- %(self.__class__.__name__, attr))
+ % (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}
+ "instead" % {'me': self.__class__.__name__, 'attr': attr}
warnings.warn(msg, DeprecationWarning, stacklevel=2)
return retattr
@@ -707,13 +782,16 @@ class dispatcher:
# and 'log_info' is for informational, warning and error logging.
def log(self, message):
+ """Log a message to stderr"""
sys.stderr.write('log: %s\n' % str(message))
def log_info(self, message, log_type='info'):
+ """Conditionally print a message"""
if log_type not in self.ignore_log_types:
- print('%s: %s' % (log_type, message))
+ print '%s: %s' % log_type, message
def handle_read_event(self):
+ """Handle a read event"""
if self.accepting:
# accepting sockets are never connected, they "spawn" new
# sockets that are connected
@@ -726,6 +804,7 @@ class dispatcher:
self.handle_read()
def handle_connect_event(self):
+ """Handle a connection event"""
err = self.socket.getsockopt(socket.SOL_SOCKET, socket.SO_ERROR)
if err != 0:
raise socket.error(err, _strerror(err))
@@ -734,6 +813,7 @@ class dispatcher:
self.connecting = False
def handle_write_event(self):
+ """Handle a write event"""
if self.accepting:
# Accepting sockets shouldn't get a write event.
# We will pretend it didn't happen.
@@ -745,6 +825,7 @@ class dispatcher:
self.handle_write()
def handle_expt_event(self):
+ """Handle expected exceptions"""
# handle_expt_event() is called if there might be an error on the
# socket, or if there is OOB data
# check for the error condition first
@@ -763,7 +844,8 @@ class dispatcher:
self.handle_expt()
def handle_error(self):
- nil, t, v, tbinfo = compact_traceback()
+ """Handle unexpected exceptions"""
+ _, t, v, tbinfo = compact_traceback()
# sometimes a user repr method will crash.
try:
@@ -777,89 +859,110 @@ class dispatcher:
t,
v,
tbinfo
- ),
+ ),
'error'
- )
+ )
self.handle_close()
- def handle_expt(self):
- self.log_info('unhandled incoming priority event', 'warning')
-
- def handle_read(self):
- self.log_info('unhandled read event', 'warning')
-
- def handle_write(self):
- self.log_info('unhandled write event', 'warning')
-
- def handle_connect(self):
- self.log_info('unhandled connect event', 'warning')
-
def handle_accept(self):
+ """Handle an accept event"""
pair = self.accept()
if pair is not None:
self.handle_accepted(*pair)
+ def handle_expt(self):
+ """Log that the subclass does not implement handle_expt"""
+ self.log_info('unhandled incoming priority event', 'warning')
+
+ def handle_read(self):
+ """Log that the subclass does not implement handle_read"""
+ self.log_info('unhandled read event', 'warning')
+
+ def handle_write(self):
+ """Log that the subclass does not implement handle_write"""
+ self.log_info('unhandled write event', 'warning')
+
+ def handle_connect(self):
+ """Log that the subclass does not implement handle_connect"""
+ self.log_info('unhandled connect event', 'warning')
+
def handle_accepted(self, sock, addr):
+ """Log that the subclass does not implement handle_accepted"""
sock.close()
self.log_info('unhandled accepted event on %s' % (addr), 'warning')
def handle_close(self):
+ """Log that the subclass does not implement handle_close"""
self.log_info('unhandled close event', 'warning')
self.close()
-# ---------------------------------------------------------------------------
-# adds simple buffered output capability, useful for simple clients.
-# [for more sophisticated usage use asynchat.async_chat]
-# ---------------------------------------------------------------------------
class dispatcher_with_send(dispatcher):
+ """
+ adds simple buffered output capability, useful for simple clients.
+ [for more sophisticated usage use asynchat.async_chat]
+ """
+ # pylint: disable=redefined-builtin
def __init__(self, sock=None, map=None):
+ # pylint: disable=redefined-builtin
+
dispatcher.__init__(self, sock, map)
self.out_buffer = b''
def initiate_send(self):
+ """Initiate a send"""
num_sent = 0
num_sent = dispatcher.send(self, self.out_buffer[:512])
self.out_buffer = self.out_buffer[num_sent:]
def handle_write(self):
+ """Handle a write event"""
self.initiate_send()
def writable(self):
- return (not self.connected) or len(self.out_buffer)
+ """Predicate to indicate if the object is writable"""
+ return not self.connected or len(self.out_buffer)
def send(self, data):
+ """Send data"""
if self.debug:
self.log_info('sending %s' % repr(data))
self.out_buffer = self.out_buffer + data
self.initiate_send()
+
# ---------------------------------------------------------------------------
# used for debugging.
# ---------------------------------------------------------------------------
+
def compact_traceback():
+ """Return a compact traceback"""
t, v, tb = sys.exc_info()
tbinfo = []
- if not tb: # Must have a traceback
+ if not tb: # Must have a traceback
raise AssertionError("traceback does not exist")
while tb:
tbinfo.append((
tb.tb_frame.f_code.co_filename,
tb.tb_frame.f_code.co_name,
str(tb.tb_lineno)
- ))
+ ))
tb = tb.tb_next
# just to be safe
del tb
- file, function, line = tbinfo[-1]
+ filename, function, line = tbinfo[-1]
info = ' '.join(['[%s|%s|%s]' % x for x in tbinfo])
- return (file, function, line), t, v, info
+ return (filename, function, line), t, v, info
+
def close_all(map=None, ignore_all=False):
+ """Close all connections"""
+ # pylint: disable=redefined-builtin
+
if map is None:
map = socket_map
for x in list(map.values()):
@@ -877,6 +980,7 @@ def close_all(map=None, ignore_all=False):
raise
map.clear()
+
# Asynchronous File I/O:
#
# After a little research (reading man pages on various unixen, and
@@ -890,27 +994,34 @@ def close_all(map=None, ignore_all=False):
#
# Regardless, this is useful for pipes, and stdin/stdout...
+
if os.name == 'posix':
import fcntl
class file_wrapper:
- # Here we override just enough to make a file
- # look like a socket for the purposes of asyncore.
- # The passed fd is automatically os.dup()'d
+ """
+ Here we override just enough to make a file look like a socket for the purposes of asyncore.
+
+ The passed fd is automatically os.dup()'d
+ """
+ # pylint: disable=old-style-class
def __init__(self, fd):
self.fd = os.dup(fd)
def recv(self, *args):
+ """Fake recv()"""
return os.read(self.fd, *args)
def send(self, *args):
+ """Fake send()"""
return os.write(self.fd, *args)
def getsockopt(self, level, optname, buflen=None):
+ """Fake getsockopt()"""
if (level == socket.SOL_SOCKET and
- optname == socket.SO_ERROR and
- not buflen):
+ optname == socket.SO_ERROR and
+ not buflen):
return 0
raise NotImplementedError("Only asyncore specific behaviour "
"implemented.")
@@ -919,14 +1030,19 @@ if os.name == 'posix':
write = send
def close(self):
+ """Fake close()"""
os.close(self.fd)
def fileno(self):
+ """Fake fileno()"""
return self.fd
class file_dispatcher(dispatcher):
+ """A dispatcher for file_wrapper objects"""
def __init__(self, fd, map=None):
+ # pylint: disable=redefined-builtin
+
dispatcher.__init__(self, None, map)
self.connected = True
try:
@@ -940,6 +1056,7 @@ if os.name == 'posix':
fcntl.fcntl(fd, fcntl.F_SETFL, flags)
def set_file(self, fd):
+ """Set file"""
self.socket = file_wrapper(fd)
self._fileno = self.socket.fileno()
self.add_channel()
diff --git a/src/network/bmproto.py b/src/network/bmproto.py
index aff6cd0c..c8927bf5 100644
--- a/src/network/bmproto.py
+++ b/src/network/bmproto.py
@@ -1,6 +1,12 @@
+# pylint: disable=too-many-return-statements,too-many-public-methods,attribute-defined-outside-init,too-many-branches
+# pylint: disable=too-many-instance-attributes,too-many-statements
+"""
+The Bitmessage Protocol
+=======================
+"""
+
import base64
import hashlib
-import random
import socket
import struct
import time
@@ -12,11 +18,11 @@ import knownnodes
from network.advanceddispatcher import AdvancedDispatcher
from network.dandelion import Dandelion
from network.bmobject import BMObject, BMObjectInsufficientPOWError, BMObjectInvalidDataError, \
- BMObjectExpiredError, BMObjectUnwantedStreamError, BMObjectInvalidError, BMObjectAlreadyHaveError
+ BMObjectExpiredError, BMObjectUnwantedStreamError, BMObjectInvalidError, BMObjectAlreadyHaveError
import network.connectionpool
from network.node import Node
from network.objectracker import ObjectTracker
-from network.proxy import Proxy, ProxyError, GeneralProxyError
+from network.proxy import ProxyError
import addresses
from queues import objectProcessorQueue, portCheckerQueue, invQueue, addrQueue
@@ -25,19 +31,27 @@ import state
import protocol
import helper_random
+
class BMProtoError(ProxyError):
+ """A Bitmessage Protocol Base Error"""
errorCodes = ("Protocol error")
class BMProtoInsufficientDataError(BMProtoError):
+ """A Bitmessage Protocol Insufficient Data Error"""
+
errorCodes = ("Insufficient data")
class BMProtoExcessiveDataError(BMProtoError):
+ """A Bitmessage Protocol Excessive Data Error"""
+
errorCodes = ("Too much data")
class BMProto(AdvancedDispatcher, ObjectTracker):
+ """A parser for the Bitmessage Protocol"""
+
# ~1.6 MB which is the maximum possible size of an inv message.
maxMessageSize = 1600100
# 2**18 = 256kB is the maximum size of an object payload
@@ -52,12 +66,15 @@ class BMProto(AdvancedDispatcher, ObjectTracker):
maxTimeOffset = 3600
def __init__(self, address=None, sock=None):
+ # pylint: disable=super-init-not-called,unused-argument
+
AdvancedDispatcher.__init__(self, sock)
self.isOutbound = False
# packet/connection from a local IP
self.local = False
def bm_proto_reset(self):
+ """TBC"""
self.magic = None
self.command = None
self.payloadLength = 0
@@ -69,7 +86,10 @@ class BMProto(AdvancedDispatcher, ObjectTracker):
self.object = None
def state_bm_header(self):
- self.magic, self.command, self.payloadLength, self.checksum = protocol.Header.unpack(self.read_buf[:protocol.Header.size])
+ """Predicate to indicate the prescence of a header"""
+
+ self.magic, self.command, self.payloadLength, self.checksum = protocol.Header.unpack(
+ self.read_buf[:protocol.Header.size])
self.command = self.command.rstrip('\x00')
if self.magic != 0xE9BEB4D9:
# skip 1 byte in order to sync
@@ -84,8 +104,9 @@ class BMProto(AdvancedDispatcher, ObjectTracker):
self.invalid = True
self.set_state("bm_command", length=protocol.Header.size, expectBytes=self.payloadLength)
return True
-
+
def state_bm_command(self):
+ """TBC"""
self.payload = self.read_buf[:self.payloadLength]
if self.checksum != hashlib.sha512(self.payload).digest()[0:4]:
logger.debug("Bad checksum, ignoring")
@@ -122,7 +143,7 @@ class BMProto(AdvancedDispatcher, ObjectTracker):
# broken read, ignore
pass
else:
- #print "Skipping command %s due to invalid data" % (self.command)
+ # print "Skipping command %s due to invalid data" % (self.command)
logger.debug("Closing due to invalid command %s", self.command)
self.close_reason = "Invalid command %s" % (self.command)
self.set_state("close")
@@ -134,16 +155,21 @@ class BMProto(AdvancedDispatcher, ObjectTracker):
return True
def decode_payload_string(self, length):
- value = self.payload[self.payloadOffset:self.payloadOffset+length]
+ """Read and return `length` bytes from payload"""
+
+ value = self.payload[self.payloadOffset:self.payloadOffset + length]
self.payloadOffset += length
return value
def decode_payload_varint(self):
+ """Decode a varint from the payload"""
+
value, offset = addresses.decodeVarint(self.payload[self.payloadOffset:])
self.payloadOffset += offset
return value
def decode_payload_node(self):
+ """Decode node details from the payload"""
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]))
@@ -153,38 +179,45 @@ class BMProto(AdvancedDispatcher, ObjectTracker):
else:
host = socket.inet_ntop(socket.AF_INET6, str(host))
if host == "":
- # 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.
host = socket.inet_ntop(socket.AF_INET, str(host[12:16]))
return Node(services, host, port)
- def decode_payload_content(self, pattern = "v"):
- # L = varint indicating the length of the next array
- # l = varint indicating the length of the next item
- # v = varint (or array)
- # H = uint16
- # I = uint32
- # Q = uint64
- # i = net_addr (without time and stream number)
- # s = string
- # 0-9 = length of the next item
- # , = end of array
+ def decode_payload_content(self, pattern="v"):
+ """
+ Decode the payload
+
+ L = varint indicating the length of the next array
+ l = varint indicating the length of the next item
+ v = varint (or array)
+ H = uint16
+ I = uint32
+ Q = uint64
+ i = net_addr (without time and stream number)
+ s = string
+ 0-9 = length of the next item
+ , = end of array
+
+ """
def decode_simple(self, char="v"):
+ """Some expected objects can be decoded very straightforwardly"""
if char == "v":
return self.decode_payload_varint()
if char == "i":
return self.decode_payload_node()
if char == "H":
self.payloadOffset += 2
- return struct.unpack(">H", self.payload[self.payloadOffset-2:self.payloadOffset])[0]
+ return struct.unpack(">H", self.payload[self.payloadOffset - 2:self.payloadOffset])[0]
if char == "I":
self.payloadOffset += 4
- return struct.unpack(">I", self.payload[self.payloadOffset-4:self.payloadOffset])[0]
+ return struct.unpack(">I", self.payload[self.payloadOffset - 4:self.payloadOffset])[0]
if char == "Q":
self.payloadOffset += 8
- return struct.unpack(">Q", self.payload[self.payloadOffset-8:self.payloadOffset])[0]
+ return struct.unpack(">Q", self.payload[self.payloadOffset - 8:self.payloadOffset])[0]
+ return None
size = None
isArray = False
@@ -197,27 +230,19 @@ class BMProto(AdvancedDispatcher, ObjectTracker):
# retval (array)
parserStack = [[1, 1, False, pattern, 0, []]]
- #try:
- # sys._getframe(200)
- # logger.error("Stack depth warning, pattern: %s", pattern)
- # return
- #except ValueError:
- # pass
-
while True:
i = parserStack[-1][3][parserStack[-1][4]]
- if i in "0123456789" and (size is None or parserStack[-1][3][parserStack[-1][4]-1] not in "lL"):
+ if i in "0123456789" and (size is None or parserStack[-1][3][parserStack[-1][4] - 1] not in "lL"):
try:
size = size * 10 + int(i)
except TypeError:
size = int(i)
isArray = False
+
elif i in "Ll" and size is None:
size = self.decode_payload_varint()
- if i == "L":
- isArray = True
- else:
- isArray = False
+ isArray = bool(i == "L")
+
elif size is not None:
if isArray:
parserStack.append([size, size, isArray, parserStack[-1][3][parserStack[-1][4]:], 0, []])
@@ -226,25 +251,26 @@ class BMProto(AdvancedDispatcher, ObjectTracker):
for j in range(parserStack[-1][4], len(parserStack[-1][3])):
if parserStack[-1][3][j] not in "lL0123456789":
break
- parserStack.append([size, size, isArray, parserStack[-1][3][parserStack[-1][4]:j+1], 0, []])
+ # pylint: disable=undefined-loop-variable
+ parserStack.append([size, size, isArray, parserStack[-1][3][parserStack[-1][4]:j + 1], 0, []])
parserStack[-2][4] += len(parserStack[-1][3]) - 1
size = None
continue
+
elif i == "s":
- #if parserStack[-2][2]:
- # parserStack[-1][5].append(self.payload[self.payloadOffset:self.payloadOffset + parserStack[-1][0]])
- #else:
parserStack[-1][5] = self.payload[self.payloadOffset:self.payloadOffset + parserStack[-1][0]]
self.payloadOffset += parserStack[-1][0]
parserStack[-1][1] = 0
parserStack[-1][2] = True
- #del parserStack[-1]
size = None
+
elif i in "viHIQ":
parserStack[-1][5].append(decode_simple(self, parserStack[-1][3][parserStack[-1][4]]))
size = None
+
else:
size = None
+
for depth in range(len(parserStack) - 1, -1, -1):
parserStack[depth][4] += 1
if parserStack[depth][4] >= len(parserStack[depth][3]):
@@ -269,16 +295,19 @@ class BMProto(AdvancedDispatcher, ObjectTracker):
raise BMProtoInsufficientDataError()
def bm_command_error(self):
+ """TBC"""
+ # pylint: disable=unused-variable
fatalStatus, banTime, inventoryVector, errorText = self.decode_payload_content("vvlsls")
logger.error("%s:%i error: %i, %s", self.destination.host, self.destination.port, fatalStatus, errorText)
return True
def bm_command_getdata(self):
+ """TBC"""
items = self.decode_payload_content("l32s")
- # skip?
+ # ..todo:: skip?
if time.time() < self.skipUntil:
return True
- #TODO make this more asynchronous
+ # .. todo:: make this more asynchronous
helper_random.randomshuffle(items)
for i in map(str, items):
if Dandelion().hasHash(i) and \
@@ -320,6 +349,7 @@ class BMProto(AdvancedDispatcher, ObjectTracker):
return True
def bm_command_inv(self):
+ """TBC"""
return self._command_inv(False)
def bm_command_dinv(self):
@@ -329,12 +359,16 @@ class BMProto(AdvancedDispatcher, ObjectTracker):
return self._command_inv(True)
def bm_command_object(self):
+ """TBC"""
objectOffset = self.payloadOffset
nonce, expiresTime, objectType, version, streamNumber = self.decode_payload_content("QQIvv")
self.object = BMObject(nonce, expiresTime, objectType, version, streamNumber, self.payload, self.payloadOffset)
if len(self.payload) - self.payloadOffset > BMProto.maxObjectPayloadSize:
- logger.info('The payload length of this object is too large (%d bytes). Ignoring it.' % (len(self.payload) - self.payloadOffset))
+ logger.info(
+ 'The payload length of this object is too large (%d bytes). Ignoring it.',
+ len(self.payload) - self.payloadOffset
+ )
raise BMProtoExcessiveDataError()
try:
@@ -347,7 +381,9 @@ class BMProto(AdvancedDispatcher, ObjectTracker):
try:
self.object.checkStream()
except (BMObjectUnwantedStreamError,) as e:
- BMProto.stopDownloadingObject(self.object.inventoryHash, BMConfigParser().get("inventory", "acceptmismatch"))
+ BMProto.stopDownloadingObject(
+ self.object.inventoryHash, BMConfigParser().get(
+ "inventory", "acceptmismatch"))
if not BMConfigParser().get("inventory", "acceptmismatch"):
raise e
@@ -366,7 +402,10 @@ class BMProto(AdvancedDispatcher, ObjectTracker):
Dandelion().removeHash(self.object.inventoryHash, "cycle detection")
Inventory()[self.object.inventoryHash] = (
- self.object.objectType, self.object.streamNumber, buffer(self.payload[objectOffset:]), self.object.expiresTime, buffer(self.object.tag))
+ self.object.objectType, self.object.streamNumber,
+ buffer(self.payload[objectOffset:]), self.object.expiresTime,
+ buffer(self.object.tag)
+ )
self.handleReceivedObject(self.object.streamNumber, self.object.inventoryHash)
invQueue.put((self.object.streamNumber, self.object.inventoryHash, self.destination))
return True
@@ -375,9 +414,10 @@ class BMProto(AdvancedDispatcher, ObjectTracker):
return self.decode_payload_content("LQIQ16sH")
def bm_command_addr(self):
- addresses = self._decode_addr()
- for i in addresses:
- seenTime, stream, services, ip, port = i
+ """TBC"""
+ these_addresses = self._decode_addr()
+ for i in these_addresses:
+ seenTime, stream, _, ip, port = i
decodedIP = protocol.checkIPAddress(str(ip))
if stream not in state.streamsInWhichIAmParticipating:
continue
@@ -402,18 +442,23 @@ class BMProto(AdvancedDispatcher, ObjectTracker):
return True
def bm_command_portcheck(self):
+ """TBC"""
portCheckerQueue.put(state.Peer(self.destination, self.peerNode.port))
return True
def bm_command_ping(self):
+ """TBC"""
self.append_write_buf(protocol.CreatePacket('pong'))
return True
def bm_command_pong(self):
- # nothing really
+ """noop"""
+ # pylint: disable=no-self-use
return True
def bm_command_verack(self):
+ """TBC"""
+
self.verackReceived = True
if self.verackSent:
if self.isSSL:
@@ -424,6 +469,8 @@ class BMProto(AdvancedDispatcher, ObjectTracker):
return True
def bm_command_version(self):
+ """TBC"""
+
self.remoteProtocolVersion, self.services, self.timestamp, self.sockNode, self.peerNode, self.nonce, \
self.userAgent, self.streams = self.decode_payload_content("IQQiiQlsLv")
self.nonce = struct.pack('>Q', self.nonce)
@@ -434,17 +481,20 @@ class BMProto(AdvancedDispatcher, ObjectTracker):
logger.debug("my external IP: %s", self.sockNode.host)
logger.debug("remote node incoming address: %s:%i", self.destination.host, self.peerNode.port)
logger.debug("user agent: %s", self.userAgent)
- logger.debug("streams: [%s]", ",".join(map(str,self.streams)))
+ logger.debug("streams: [%s]", ",".join(map(str, self.streams)))
if not self.peerValidityChecks():
- # TODO ABORT
+ # .. todo:: ABORT
return True
- #shared.connectedHostsList[self.destination] = self.streams[0]
self.append_write_buf(protocol.CreatePacket('verack'))
self.verackSent = True
if not self.isOutbound:
- self.append_write_buf(protocol.assembleVersionMessage(self.destination.host, self.destination.port, \
- network.connectionpool.BMConnectionPool().streams, True, nodeid=self.nodeid))
- #print "%s:%i: Sending version" % (self.destination.host, self.destination.port)
+ self.append_write_buf(
+ protocol.assembleVersionMessage(
+ self.destination.host,
+ self.destination.port,
+ network.connectionpool.BMConnectionPool().streams,
+ True,
+ nodeid=self.nodeid))
if ((self.services & protocol.NODE_SSL == protocol.NODE_SSL) and
protocol.haveSSL(not self.isOutbound)):
self.isSSL = True
@@ -457,41 +507,45 @@ class BMProto(AdvancedDispatcher, ObjectTracker):
return True
def peerValidityChecks(self):
+ """Check the validity of peers"""
if self.remoteProtocolVersion < 3:
- self.append_write_buf(protocol.assembleErrorMessage(fatal=2,
- errorText="Your is using an old protocol. Closing connection."))
- logger.debug ('Closing connection to old protocol version %s, node: %s',
- str(self.remoteProtocolVersion), str(self.destination))
+ self.append_write_buf(protocol.assembleErrorMessage(
+ fatal=2, errorText="Your is using an old protocol. Closing connection."))
+ logger.debug('Closing connection to old protocol version %s, node: %s',
+ str(self.remoteProtocolVersion), str(self.destination))
return False
if self.timeOffset > BMProto.maxTimeOffset:
- self.append_write_buf(protocol.assembleErrorMessage(fatal=2,
- errorText="Your time is too far in the future compared to mine. Closing connection."))
+ self.append_write_buf(
+ protocol.assembleErrorMessage(
+ fatal=2,
+ errorText="Your time is too far in the future compared to mine. Closing connection."))
logger.info("%s's time is too far in the future (%s seconds). Closing connection to it.",
- self.destination, self.timeOffset)
+ self.destination, self.timeOffset)
shared.timeOffsetWrongCount += 1
return False
elif self.timeOffset < -BMProto.maxTimeOffset:
- self.append_write_buf(protocol.assembleErrorMessage(fatal=2,
- errorText="Your time is too far in the past compared to mine. Closing connection."))
+ self.append_write_buf(protocol.assembleErrorMessage(
+ fatal=2, errorText="Your time is too far in the past compared to mine. Closing connection."))
logger.info("%s's time is too far in the past (timeOffset %s seconds). Closing connection to it.",
- self.destination, self.timeOffset)
+ self.destination, self.timeOffset)
shared.timeOffsetWrongCount += 1
return False
else:
shared.timeOffsetWrongCount = 0
if not self.streams:
- self.append_write_buf(protocol.assembleErrorMessage(fatal=2,
- errorText="We don't have shared stream interests. Closing connection."))
- logger.debug ('Closed connection to %s because there is no overlapping interest in streams.',
- str(self.destination))
+ self.append_write_buf(protocol.assembleErrorMessage(
+ fatal=2, errorText="We don't have shared stream interests. Closing connection."))
+ logger.debug('Closed connection to %s because there is no overlapping interest in streams.',
+ str(self.destination))
return False
if self.destination in network.connectionpool.BMConnectionPool().inboundConnections:
try:
if not protocol.checkSocksIP(self.destination.host):
- self.append_write_buf(protocol.assembleErrorMessage(fatal=2,
- errorText="Too many connections from your IP. Closing connection."))
- logger.debug ('Closed connection to %s because we are already connected to that IP.',
- str(self.destination))
+ self.append_write_buf(
+ protocol.assembleErrorMessage(
+ fatal=2, errorText="Too many connections from your IP. Closing connection."))
+ logger.debug('Closed connection to %s because we are already connected to that IP.',
+ str(self.destination))
return False
except:
pass
@@ -499,27 +553,30 @@ class BMProto(AdvancedDispatcher, ObjectTracker):
# incoming from a peer we're connected to as outbound, or server full
# report the same error to counter deanonymisation
if state.Peer(self.destination.host, self.peerNode.port) in \
- network.connectionpool.BMConnectionPool().inboundConnections or \
- len(network.connectionpool.BMConnectionPool().inboundConnections) + \
- len(network.connectionpool.BMConnectionPool().outboundConnections) > \
- BMConfigParser().safeGetInt("bitmessagesettings", "maxtotalconnections") + \
- BMConfigParser().safeGetInt("bitmessagesettings", "maxbootstrapconnections"):
+ network.connectionpool.BMConnectionPool().inboundConnections or \
+ len(network.connectionpool.BMConnectionPool().inboundConnections) + \
+ len(network.connectionpool.BMConnectionPool().outboundConnections) > \
+ BMConfigParser().safeGetInt("bitmessagesettings", "maxtotalconnections") + \
+ BMConfigParser().safeGetInt("bitmessagesettings", "maxbootstrapconnections"):
self.append_write_buf(protocol.assembleErrorMessage(fatal=2,
- errorText="Server full, please try again later."))
- logger.debug ("Closed connection to %s due to server full or duplicate inbound/outbound.",
- str(self.destination))
+ errorText="Server full, please try again later."))
+ logger.debug("Closed connection to %s due to server full or duplicate inbound/outbound.",
+ str(self.destination))
return False
if network.connectionpool.BMConnectionPool().isAlreadyConnected(self.nonce):
- self.append_write_buf(protocol.assembleErrorMessage(fatal=2,
- errorText="I'm connected to myself. Closing connection."))
- logger.debug ("Closed connection to %s because I'm connected to myself.",
- str(self.destination))
+ self.append_write_buf(
+ protocol.assembleErrorMessage(
+ fatal=2,
+ errorText="I'm connected to myself. Closing connection."))
+ logger.debug("Closed connection to %s because I'm connected to myself.",
+ str(self.destination))
return False
return True
@staticmethod
def assembleAddr(peerList):
+ """iBuild up a packed address"""
if isinstance(peerList, state.Peer):
peerList = (peerList)
if not peerList:
@@ -541,6 +598,7 @@ class BMProto(AdvancedDispatcher, ObjectTracker):
@staticmethod
def stopDownloadingObject(hashId, forwardAnyway=False):
+ """Stop downloading an object"""
for connection in network.connectionpool.BMConnectionPool().inboundConnections.values() + \
network.connectionpool.BMConnectionPool().outboundConnections.values():
try:
@@ -559,6 +617,7 @@ class BMProto(AdvancedDispatcher, ObjectTracker):
pass
def handle_close(self):
+ """Handle close"""
self.set_state("close")
if not (self.accepting or self.connecting or self.connected):
# already disconnected
diff --git a/src/network/tcp.py b/src/network/tcp.py
index 163cbd85..322acba6 100644
--- a/src/network/tcp.py
+++ b/src/network/tcp.py
@@ -1,41 +1,45 @@
-import base64
-from binascii import hexlify
-import hashlib
+# pylint: disable=too-many-ancestors
+"""
+tcp.py
+======
+"""
+
+from __future__ import absolute_import
+
import math
import time
-from pprint import pprint
import socket
-import struct
import random
-import traceback
-from addresses import calculateInventoryHash
from debug import logger
from helper_random import randomBytes
import helper_random
from inventory import Inventory
import knownnodes
from network.advanceddispatcher import AdvancedDispatcher
-from network.bmproto import BMProtoError, BMProtoInsufficientDataError, BMProtoExcessiveDataError, BMProto
-from network.bmobject import BMObject, BMObjectInsufficientPOWError, BMObjectInvalidDataError, BMObjectExpiredError, BMObjectUnwantedStreamError, BMObjectInvalidError, BMObjectAlreadyHaveError
+
+from network.bmproto import BMProto
import network.connectionpool
from network.dandelion import Dandelion
-from network.node import Node
import network.asyncore_pollchoose as asyncore
-from network.proxy import Proxy, ProxyError, GeneralProxyError
from network.objectracker import ObjectTracker
-from network.socks5 import Socks5Connection, Socks5Resolver, Socks5AuthError, Socks5Error
-from network.socks4a import Socks4aConnection, Socks4aResolver, Socks4aError
+from network.socks5 import Socks5Connection
+from network.socks4a import Socks4aConnection
from network.tls import TLSDispatcher
-
import addresses
from bmconfigparser import BMConfigParser
-from queues import invQueue, objectProcessorQueue, portCheckerQueue, UISignalQueue, receiveDataQueue
+from queues import invQueue, UISignalQueue, receiveDataQueue
import shared
import state
import protocol
-class TCPConnection(BMProto, TLSDispatcher):
+
+class TCPConnection(BMProto, TLSDispatcher): # pylint: disable=too-many-instance-attributes
+ """
+
+ .. todo:: Look to understand and/or fix the non-parent-init-called
+ """
+
def __init__(self, address=None, sock=None):
BMProto.__init__(self, address=address, sock=sock)
self.verackReceived = False
@@ -67,18 +71,27 @@ class TCPConnection(BMProto, TLSDispatcher):
self.connect(self.destination)
logger.debug("Connecting to %s:%i", self.destination.host, self.destination.port)
encodedAddr = protocol.encodeHost(self.destination.host)
- if protocol.checkIPAddress(encodedAddr, True) and not protocol.checkSocksIP(self.destination.host):
- self.local = True
- else:
- self.local = False
- #shared.connectedHostsList[self.destination] = 0
- ObjectTracker.__init__(self)
+ self.local = all([
+ protocol.checkIPAddress(encodedAddr, True),
+ not protocol.checkSocksIP(self.destination.host)
+ ])
+ ObjectTracker.__init__(self) # pylint: disable=non-parent-init-called
self.bm_proto_reset()
self.set_state("bm_header", expectBytes=protocol.Header.size)
- def antiIntersectionDelay(self, initial = False):
+ def antiIntersectionDelay(self, initial=False):
+ """TBC"""
# estimated time for a small object to propagate across the whole network
- delay = math.ceil(math.log(max(len(knownnodes.knownNodes[x]) for x in knownnodes.knownNodes) + 2, 20)) * (0.2 + invQueue.queueCount/2.0)
+
+ delay = math.ceil(
+ math.log(
+ max(
+ len(knownnodes.knownNodes[x])
+ for x in knownnodes.knownNodes
+ ) + 2,
+ 20
+ )
+ ) * (0.2 + invQueue.queueCount / 2.0)
# take the stream with maximum amount of nodes
# +2 is to avoid problems with log(0) and log(1)
# 20 is avg connected nodes count
@@ -93,12 +106,14 @@ class TCPConnection(BMProto, TLSDispatcher):
self.skipUntil = time.time() + delay
def state_connection_fully_established(self):
+ """TBC"""
self.set_connection_fully_established()
self.set_state("bm_header")
self.bm_proto_reset()
return True
def set_connection_fully_established(self):
+ """TBC"""
if not self.isOutbound and not self.local:
shared.clientHasReceivedIncomingConnections = True
UISignalQueue.put(('setStatusIcon', 'green'))
@@ -113,50 +128,50 @@ class TCPConnection(BMProto, TLSDispatcher):
self.sendBigInv()
def sendAddr(self):
+ """TBC"""
# We are going to share a maximum number of 1000 addrs (per overlapping
# stream) with our peer. 500 from overlapping streams, 250 from the
# left child stream, and 250 from the right child stream.
maxAddrCount = BMConfigParser().safeGetInt("bitmessagesettings", "maxaddrperstreamsend", 500)
# init
- addressCount = 0
- payload = b''
-
templist = []
addrs = {}
for stream in self.streams:
with knownnodes.knownNodesLock:
- if len(knownnodes.knownNodes[stream]) > 0:
+ if knownnodes.knownNodes[stream]:
filtered = {k: v for k, v in knownnodes.knownNodes[stream].items()
- if v["lastseen"] > (int(time.time()) - shared.maximumAgeOfNodesThatIAdvertiseToOthers)}
+ if v["lastseen"] > (int(time.time()) - shared.maximumAgeOfNodesThatIAdvertiseToOthers)}
elemCount = len(filtered)
if elemCount > maxAddrCount:
elemCount = maxAddrCount
# only if more recent than 3 hours
addrs[stream] = helper_random.randomsample(filtered.items(), elemCount)
# sent 250 only if the remote isn't interested in it
- if len(knownnodes.knownNodes[stream * 2]) > 0 and stream not in self.streams:
- filtered = {k: v for k, v in knownnodes.knownNodes[stream*2].items()
- if v["lastseen"] > (int(time.time()) - shared.maximumAgeOfNodesThatIAdvertiseToOthers)}
+ if knownnodes.knownNodes[stream * 2] and stream not in self.streams:
+ filtered = {k: v for k, v in knownnodes.knownNodes[stream * 2].items()
+ if v["lastseen"] > (int(time.time()) - shared.maximumAgeOfNodesThatIAdvertiseToOthers)}
elemCount = len(filtered)
if elemCount > maxAddrCount / 2:
elemCount = int(maxAddrCount / 2)
addrs[stream * 2] = helper_random.randomsample(filtered.items(), elemCount)
- if len(knownnodes.knownNodes[(stream * 2) + 1]) > 0 and stream not in self.streams:
- filtered = {k: v for k, v in knownnodes.knownNodes[stream*2+1].items()
- if v["lastseen"] > (int(time.time()) - shared.maximumAgeOfNodesThatIAdvertiseToOthers)}
+ if knownnodes.knownNodes[(stream * 2) + 1] and stream not in self.streams:
+ filtered = {k: v for k, v in knownnodes.knownNodes[stream * 2 + 1].items()
+ if v["lastseen"] > (int(time.time()) - shared.maximumAgeOfNodesThatIAdvertiseToOthers)}
elemCount = len(filtered)
if elemCount > maxAddrCount / 2:
elemCount = int(maxAddrCount / 2)
addrs[stream * 2 + 1] = helper_random.randomsample(filtered.items(), elemCount)
- for substream in addrs.keys():
+ for substream in addrs:
for peer, params in addrs[substream]:
templist.append((substream, peer, params["lastseen"]))
- if len(templist) > 0:
+ if templist:
self.append_write_buf(BMProto.assembleAddr(templist))
def sendBigInv(self):
+ """TBC"""
def sendChunk():
+ """TBC"""
if objectCount == 0:
return
logger.debug('Sending huge inv message with %i objects to just this one peer', objectCount)
@@ -172,13 +187,12 @@ class TCPConnection(BMProto, TLSDispatcher):
if Dandelion().hasHash(objHash):
continue
bigInvList[objHash] = 0
- #self.objectsNewToThem[objHash] = time.time()
objectCount = 0
payload = b''
# Now let us start appending all of these hashes together. They will be
# sent out in a big inv message to our new peer.
- for hash, storedValue in bigInvList.items():
- payload += hash
+ for obj_hash, _ in bigInvList.items():
+ payload += obj_hash
objectCount += 1
if objectCount >= BMProto.maxObjectCount:
sendChunk()
@@ -189,20 +203,26 @@ class TCPConnection(BMProto, TLSDispatcher):
sendChunk()
def handle_connect(self):
+ """TBC"""
try:
AdvancedDispatcher.handle_connect(self)
except socket.error as e:
- if e.errno in asyncore._DISCONNECTED:
- logger.debug("%s:%i: Connection failed: %s" % (self.destination.host, self.destination.port, str(e)))
+ if e.errno in asyncore._DISCONNECTED: # pylint: disable=protected-access
+ logger.debug("%s:%i: Connection failed: %s", self.destination.host, self.destination.port, str(e))
return
self.nodeid = randomBytes(8)
- self.append_write_buf(protocol.assembleVersionMessage(self.destination.host, self.destination.port, \
- network.connectionpool.BMConnectionPool().streams, False, nodeid=self.nodeid))
- #print "%s:%i: Sending version" % (self.destination.host, self.destination.port)
+ self.append_write_buf(
+ protocol.assembleVersionMessage(
+ self.destination.host,
+ self.destination.port,
+ network.connectionpool.BMConnectionPool().streams,
+ False,
+ nodeid=self.nodeid))
self.connectedAt = time.time()
receiveDataQueue.put(self.destination)
def handle_read(self):
+ """TBC"""
TLSDispatcher.handle_read(self)
if self.isOutbound and self.fullyEstablished:
for s in self.streams:
@@ -214,9 +234,11 @@ class TCPConnection(BMProto, TLSDispatcher):
receiveDataQueue.put(self.destination)
def handle_write(self):
+ """TBC"""
TLSDispatcher.handle_write(self)
def handle_close(self):
+ """TBC"""
if self.isOutbound and not self.fullyEstablished:
knownnodes.decreaseRating(self.destination)
if self.fullyEstablished:
@@ -227,37 +249,55 @@ class TCPConnection(BMProto, TLSDispatcher):
class Socks5BMConnection(Socks5Connection, TCPConnection):
+ """TBC"""
+
def __init__(self, address):
Socks5Connection.__init__(self, address=address)
TCPConnection.__init__(self, address=address, sock=self.socket)
self.set_state("init")
def state_proxy_handshake_done(self):
+ """TBC"""
Socks5Connection.state_proxy_handshake_done(self)
self.nodeid = randomBytes(8)
- self.append_write_buf(protocol.assembleVersionMessage(self.destination.host, self.destination.port, \
- network.connectionpool.BMConnectionPool().streams, False, nodeid=self.nodeid))
+ self.append_write_buf(
+ protocol.assembleVersionMessage(
+ self.destination.host,
+ self.destination.port,
+ network.connectionpool.BMConnectionPool().streams,
+ False,
+ nodeid=self.nodeid))
self.set_state("bm_header", expectBytes=protocol.Header.size)
return True
class Socks4aBMConnection(Socks4aConnection, TCPConnection):
+ """TBC"""
+
def __init__(self, address):
Socks4aConnection.__init__(self, address=address)
TCPConnection.__init__(self, address=address, sock=self.socket)
self.set_state("init")
def state_proxy_handshake_done(self):
+ """TBC"""
Socks4aConnection.state_proxy_handshake_done(self)
self.nodeid = randomBytes(8)
- self.append_write_buf(protocol.assembleVersionMessage(self.destination.host, self.destination.port, \
- network.connectionpool.BMConnectionPool().streams, False, nodeid=self.nodeid))
+ self.append_write_buf(
+ protocol.assembleVersionMessage(
+ self.destination.host,
+ self.destination.port,
+ network.connectionpool.BMConnectionPool().streams,
+ False,
+ nodeid=self.nodeid))
self.set_state("bm_header", expectBytes=protocol.Header.size)
return True
class TCPServer(AdvancedDispatcher):
- def __init__(self, host='127.0.0.1', port=8444):
+ """TBC"""
+
+ def __init__(self, host='127.0.0.1', port=8444): # pylint: disable=redefined-outer-name
if not hasattr(self, '_map'):
AdvancedDispatcher.__init__(self)
self.create_socket(socket.AF_INET, socket.SOCK_STREAM)
@@ -280,20 +320,22 @@ class TCPServer(AdvancedDispatcher):
self.listen(5)
def is_bound(self):
+ """TBC"""
try:
return self.bound
except AttributeError:
return False
def handle_accept(self):
+ """TBC"""
pair = self.accept()
if pair is not None:
- sock, addr = pair
+ sock, _ = pair
state.ownAddresses[state.Peer(sock.getsockname()[0], sock.getsockname()[1])] = True
if len(network.connectionpool.BMConnectionPool().inboundConnections) + \
- len(network.connectionpool.BMConnectionPool().outboundConnections) > \
- BMConfigParser().safeGetInt("bitmessagesettings", "maxtotalconnections") + \
- BMConfigParser().safeGetInt("bitmessagesettings", "maxbootstrapconnections") + 10:
+ len(network.connectionpool.BMConnectionPool().outboundConnections) > \
+ BMConfigParser().safeGetInt("bitmessagesettings", "maxtotalconnections") + \
+ BMConfigParser().safeGetInt("bitmessagesettings", "maxbootstrapconnections") + 10:
# 10 is a sort of buffer, in between it will go through the version handshake
# and return an error to the peer
logger.warning("Server full, dropping connection")
@@ -310,17 +352,7 @@ if __name__ == "__main__":
for host in (("127.0.0.1", 8448),):
direct = TCPConnection(host)
- while len(asyncore.socket_map) > 0:
+ while asyncore.socket_map:
print "loop, state = %s" % (direct.state)
asyncore.loop(timeout=10, count=1)
continue
-
- proxy = Socks5BMConnection(host)
- while len(asyncore.socket_map) > 0:
-# print "loop, state = %s" % (proxy.state)
- asyncore.loop(timeout=10, count=1)
-
- proxy = Socks4aBMConnection(host)
- while len(asyncore.socket_map) > 0:
-# print "loop, state = %s" % (proxy.state)
- asyncore.loop(timeout=10, count=1)
diff --git a/src/proofofwork.py b/src/proofofwork.py
index df6ed295..aeb782c8 100644
--- a/src/proofofwork.py
+++ b/src/proofofwork.py
@@ -1,60 +1,74 @@
-#import shared
-#import time
-#from multiprocessing import Pool, cpu_count
+# pylint: disable=too-many-branches,too-many-statements,protected-access
+"""
+proofofwork.py
+==============
+"""
+
+from __future__ import absolute_import
+
+import ctypes
import hashlib
-from struct import unpack, pack
-from subprocess import call
+import os
import sys
import time
-from bmconfigparser import BMConfigParser
-from debug import logger
+from struct import unpack, pack
+from subprocess import call
+
import paths
import openclpow
import queues
import tr
-import os
-import ctypes
-
import state
+from bmconfigparser import BMConfigParser
+from debug import logger
+
bitmsglib = 'bitmsghash.so'
-
bmpow = None
+
def _set_idle():
if 'linux' in sys.platform:
os.nice(20)
else:
try:
+ # pylint: disable=no-member,import-error
sys.getwindowsversion()
- import win32api,win32process,win32con # @UnresolvedImport
+ import win32api
+ import win32process
+ import win32con # @UnresolvedImport
pid = win32api.GetCurrentProcessId()
handle = win32api.OpenProcess(win32con.PROCESS_ALL_ACCESS, True, pid)
win32process.SetPriorityClass(handle, win32process.IDLE_PRIORITY_CLASS)
except:
- #Windows 64-bit
+ # Windows 64-bit
pass
+
def _pool_worker(nonce, initialHash, target, pool_size):
_set_idle()
trialValue = float('inf')
while trialValue > target:
nonce += pool_size
- trialValue, = unpack('>Q',hashlib.sha512(hashlib.sha512(pack('>Q',nonce) + initialHash).digest()).digest()[0:8])
+ trialValue, = unpack('>Q', hashlib.sha512(hashlib.sha512(
+ pack('>Q', nonce) + initialHash).digest()).digest()[0:8])
return [trialValue, nonce]
+
def _doSafePoW(target, initialHash):
logger.debug("Safe PoW start")
nonce = 0
trialValue = float('inf')
while trialValue > target and state.shutdown == 0:
nonce += 1
- trialValue, = unpack('>Q',hashlib.sha512(hashlib.sha512(pack('>Q',nonce) + initialHash).digest()).digest()[0:8])
+ trialValue, = unpack('>Q', hashlib.sha512(hashlib.sha512(
+ pack('>Q', nonce) + initialHash).digest()).digest()[0:8])
if state.shutdown != 0:
- raise StopIteration("Interrupted")
+ raise StopIteration("Interrupted") # pylint: misplaced-bare-raise
logger.debug("Safe PoW done")
return [trialValue, nonce]
+
def _doFastPoW(target, initialHash):
logger.debug("Fast PoW start")
from multiprocessing import Pool, cpu_count
@@ -96,7 +110,8 @@ def _doFastPoW(target, initialHash):
logger.debug("Fast PoW done")
return result[0], result[1]
time.sleep(0.2)
-
+
+
def _doCPoW(target, initialHash):
h = initialHash
m = target
@@ -104,33 +119,52 @@ def _doCPoW(target, initialHash):
out_m = ctypes.c_ulonglong(m)
logger.debug("C PoW start")
nonce = bmpow(out_h, out_m)
- trialValue, = unpack('>Q',hashlib.sha512(hashlib.sha512(pack('>Q',nonce) + initialHash).digest()).digest()[0:8])
+ trialValue, = unpack('>Q', hashlib.sha512(hashlib.sha512(pack('>Q', nonce) + initialHash).digest()).digest()[0:8])
if state.shutdown != 0:
raise StopIteration("Interrupted")
logger.debug("C PoW done")
return [trialValue, nonce]
+
def _doGPUPoW(target, initialHash):
logger.debug("GPU PoW start")
nonce = openclpow.do_opencl_pow(initialHash.encode("hex"), target)
- trialValue, = unpack('>Q',hashlib.sha512(hashlib.sha512(pack('>Q',nonce) + initialHash).digest()).digest()[0:8])
- #print "{} - value {} < {}".format(nonce, trialValue, target)
+ trialValue, = unpack('>Q', hashlib.sha512(hashlib.sha512(pack('>Q', nonce) + initialHash).digest()).digest()[0:8])
if trialValue > target:
deviceNames = ", ".join(gpu.name for gpu in openclpow.enabledGpus)
- queues.UISignalQueue.put(('updateStatusBar', (tr._translate("MainWindow",'Your GPU(s) did not calculate correctly, disabling OpenCL. Please report to the developers.'), 1)))
- logger.error("Your GPUs (%s) did not calculate correctly, disabling OpenCL. Please report to the developers.", deviceNames)
+ queues.UISignalQueue.put(
+ (
+ 'updateStatusBar',
+ (
+ tr._translate(
+ "MainWindow",
+ 'Your GPU(s) did not calculate correctly, disabling OpenCL. Please report to the developers.'
+ ),
+ 1
+ )
+ )
+ )
+ logger.error(
+ "Your GPUs (%s) did not calculate correctly, disabling OpenCL. Please report to the developers.",
+ deviceNames)
openclpow.enabledGpus = []
raise Exception("GPU did not calculate correctly.")
if state.shutdown != 0:
raise StopIteration("Interrupted")
logger.debug("GPU PoW done")
return [trialValue, nonce]
-
-def estimate(difficulty, format = False):
+
+
+def estimate(difficulty, format=False): # pylint: disable=redefined-builtin
+ """
+ .. todo: fix unused variable
+ """
ret = difficulty / 10
if ret < 1:
ret = 1
+
if format:
+ # pylint: disable=unused-variable
out = str(int(ret)) + " seconds"
if ret > 60:
ret /= 60
@@ -148,25 +182,46 @@ def estimate(difficulty, format = False):
if ret > 366:
ret /= 366
out = str(int(ret)) + " years"
- else:
- return ret
+ ret = None # Ensure legacy behaviour
+
+ return ret
+
def getPowType():
+ """Get the proof of work implementation"""
+
if openclpow.openclEnabled():
return "OpenCL"
if bmpow:
return "C"
return "python"
+
def notifyBuild(tried=False):
+ """TBC"""
+
if bmpow:
- queues.UISignalQueue.put(('updateStatusBar', (tr._translate("proofofwork", "C PoW module built successfully."), 1)))
+ queues.UISignalQueue.put(('updateStatusBar', (tr._translate(
+ "proofofwork", "C PoW module built successfully."), 1)))
elif tried:
- queues.UISignalQueue.put(('updateStatusBar', (tr._translate("proofofwork", "Failed to build C PoW module. Please build it manually."), 1)))
+ queues.UISignalQueue.put(
+ (
+ 'updateStatusBar', (
+ tr._translate(
+ "proofofwork",
+ "Failed to build C PoW module. Please build it manually."
+ ),
+ 1
+ )
+ )
+ )
else:
- queues.UISignalQueue.put(('updateStatusBar', (tr._translate("proofofwork", "C PoW module unavailable. Please build it."), 1)))
+ queues.UISignalQueue.put(('updateStatusBar', (tr._translate(
+ "proofofwork", "C PoW module unavailable. Please build it."), 1)))
+
def buildCPoW():
+ """TBC"""
if bmpow is not None:
return
if paths.frozen is not None:
@@ -190,29 +245,27 @@ def buildCPoW():
except:
notifyBuild(True)
+
def run(target, initialHash):
+ """Run the proof of work thread"""
+
if state.shutdown != 0:
raise
target = int(target)
if openclpow.openclEnabled():
-# trialvalue1, nonce1 = _doGPUPoW(target, initialHash)
-# trialvalue, nonce = _doFastPoW(target, initialHash)
-# print "GPU: %s, %s" % (trialvalue1, nonce1)
-# print "Fast: %s, %s" % (trialvalue, nonce)
-# return [trialvalue, nonce]
try:
return _doGPUPoW(target, initialHash)
except StopIteration:
raise
except:
- pass # fallback
+ pass # fallback
if bmpow:
try:
return _doCPoW(target, initialHash)
except StopIteration:
raise
except:
- pass # fallback
+ pass # fallback
if paths.frozen == "macosx_app" or not paths.frozen:
# on my (Peter Surda) Windows 10, Windows Defender
# does not like this and fights with PyBitmessage
@@ -225,24 +278,30 @@ def run(target, initialHash):
raise
except:
logger.error("Fast PoW got exception:", exc_info=True)
- pass #fallback
try:
return _doSafePoW(target, initialHash)
except StopIteration:
raise
except:
- pass #fallback
+ pass # fallback
+
def resetPoW():
+ """TBC"""
openclpow.initCL()
+
# init
+
+
def init():
- global bitmsglib, bso, bmpow
+ """TBC"""
+ # pylint: disable=global-statement
+ global bitmsglib, bmpow
openclpow.initCL()
- if "win32" == sys.platform:
+ if sys.platform == "win32":
if ctypes.sizeof(ctypes.c_voidp) == 4:
bitmsglib = 'bitmsghash32.dll'
else:
diff --git a/src/protocol.py b/src/protocol.py
index 5c143d2d..dca4c942 100644
--- a/src/protocol.py
+++ b/src/protocol.py
@@ -168,25 +168,13 @@ def checkSocksIP(host):
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
- Arguments:
- int nonceTrialsPerByte (default: from default.py)
- int payloadLengthExtraBytes (default: from default.py)
- float recvTime (optional) UNIX epoch time when object was
- received from the network (default: current system time)
- Returns:
- True if PoW valid and sufficient, False in all other cases
- """
+ payloadLengthExtraBytes=0):
if nonceTrialsPerByte < defaults.networkDefaultProofOfWorkNonceTrialsPerByte:
nonceTrialsPerByte = defaults.networkDefaultProofOfWorkNonceTrialsPerByte
if payloadLengthExtraBytes < defaults.networkDefaultPayloadLengthExtraBytes:
payloadLengthExtraBytes = defaults.networkDefaultPayloadLengthExtraBytes
endOfLifeTime, = unpack('>Q', data[8:16])
- TTL = endOfLifeTime - (int(recvTime) if recvTime else int(time.time()))
+ TTL = endOfLifeTime - int(time.time())
if TTL < 300:
TTL = 300
POW, = unpack('>Q', hashlib.sha512(hashlib.sha512(data[
diff --git a/src/socks/__init__.py b/src/socks/__init__.py
index 0bfa18f5..aa83f60e 100644
--- a/src/socks/__init__.py
+++ b/src/socks/__init__.py
@@ -1,4 +1,6 @@
-"""SocksiPy - Python SOCKS module.
+# pylint: disable=too-many-arguments,global-statement,too-many-branches
+"""
+SocksiPy - Python SOCKS module.
Version 1.00
Copyright 2006 Dan-Haim. All rights reserved.
@@ -28,10 +30,6 @@ OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMANGE.
This module provides a standard socket-like interface for Python
for tunneling connections through SOCKS proxies.
-"""
-
-"""
-
Minor modifications made by Christopher Gilbert (http://motomastyle.com/)
for use in PyLoris (http://pyloris.sourceforge.net/)
@@ -42,7 +40,7 @@ mainly to merge bug fixes found in Sourceforge
import socket
import struct
-import sys
+
PROXY_TYPE_SOCKS4 = 1
PROXY_TYPE_SOCKS5 = 2
@@ -51,46 +49,71 @@ PROXY_TYPE_HTTP = 3
_defaultproxy = None
_orgsocket = socket.socket
-class ProxyError(Exception): pass
-class GeneralProxyError(ProxyError): pass
-class Socks5AuthError(ProxyError): pass
-class Socks5Error(ProxyError): pass
-class Socks4Error(ProxyError): pass
-class HTTPError(ProxyError): pass
+
+class ProxyError(Exception):
+ """Base class for other ProxyErrors"""
+ pass
+
+
+class GeneralProxyError(ProxyError):
+ """Handle a general proxy error"""
+ pass
+
+
+class Socks5AuthError(ProxyError):
+ """Handle a SOCKS5 auth error"""
+ pass
+
+
+class Socks5Error(ProxyError):
+ """Handle a SOCKS5 non-auth error"""
+ pass
+
+
+class Socks4Error(ProxyError):
+ """Handle a SOCKS4 error"""
+ pass
+
+
+class HTTPError(ProxyError):
+ """Handle a HTTP error"""
+ pass
+
_generalerrors = ("success",
- "invalid data",
- "not connected",
- "not available",
- "bad proxy type",
- "bad input",
- "timed out",
- "network unreachable",
- "connection refused",
- "host unreachable")
+ "invalid data",
+ "not connected",
+ "not available",
+ "bad proxy type",
+ "bad input",
+ "timed out",
+ "network unreachable",
+ "connection refused",
+ "host unreachable")
_socks5errors = ("succeeded",
- "general SOCKS server failure",
- "connection not allowed by ruleset",
- "Network unreachable",
- "Host unreachable",
- "Connection refused",
- "TTL expired",
- "Command not supported",
- "Address type not supported",
- "Unknown error")
+ "general SOCKS server failure",
+ "connection not allowed by ruleset",
+ "Network unreachable",
+ "Host unreachable",
+ "Connection refused",
+ "TTL expired",
+ "Command not supported",
+ "Address type not supported",
+ "Unknown error")
_socks5autherrors = ("succeeded",
- "authentication is required",
- "all offered authentication methods were rejected",
- "unknown username or invalid password",
- "unknown error")
+ "authentication is required",
+ "all offered authentication methods were rejected",
+ "unknown username or invalid password",
+ "unknown error")
_socks4errors = ("request granted",
- "request rejected or failed",
- "request rejected because SOCKS server cannot connect to identd on the client",
- "request rejected because the client program and identd report different user-ids",
- "unknown error")
+ "request rejected or failed",
+ "request rejected because SOCKS server cannot connect to identd on the client",
+ "request rejected because the client program and identd report different user-ids",
+ "unknown error")
+
def setdefaultproxy(proxytype=None, addr=None, port=None, rdns=True, username=None, password=None):
"""setdefaultproxy(proxytype, addr[, port[, rdns[, username[, password]]]])
@@ -100,6 +123,7 @@ def setdefaultproxy(proxytype=None, addr=None, port=None, rdns=True, username=No
global _defaultproxy
_defaultproxy = (proxytype, addr, port, rdns, username, password)
+
def wrapmodule(module):
"""wrapmodule(module)
Attempts to replace a module's socket library with a SOCKS socket. Must set
@@ -107,11 +131,12 @@ def wrapmodule(module):
This will only work on modules that import socket directly into the namespace;
most of the Python Standard Library falls into this category.
"""
- if _defaultproxy != None:
+ if _defaultproxy is not None:
module.socket.socket = socksocket
else:
raise GeneralProxyError((4, "no proxy specified"))
+
class socksocket(socket.socket):
"""socksocket([family[, type[, proto]]]) -> socket object
Open a SOCKS enabled socket. The parameters are the same as
@@ -120,8 +145,9 @@ class socksocket(socket.socket):
"""
def __init__(self, family=socket.AF_INET, type=socket.SOCK_STREAM, proto=0, _sock=None):
+ # pylint: disable=redefined-builtin
_orgsocket.__init__(self, family, type, proto, _sock)
- if _defaultproxy != None:
+ if _defaultproxy is not None:
self.__proxy = _defaultproxy
else:
self.__proxy = (None, None, None, None, None, None)
@@ -138,8 +164,9 @@ class socksocket(socket.socket):
except socket.timeout:
raise GeneralProxyError((6, "timed out"))
while len(data) < count:
- d = self.recv(count-len(data))
- if not d: raise GeneralProxyError((0, "connection closed unexpectedly"))
+ d = self.recv(count - len(data))
+ if not d:
+ raise GeneralProxyError((0, "connection closed unexpectedly"))
data = data + d
return data
@@ -167,7 +194,7 @@ class socksocket(socket.socket):
Negotiates a connection through a SOCKS5 server.
"""
# First we'll send the authentication packages we support.
- if (self.__proxy[4]!=None) and (self.__proxy[5]!=None):
+ if (self.__proxy[4] is not None) and (self.__proxy[5] is not None):
# The username/password details were supplied to the
# setproxy method so we support the USERNAME/PASSWORD
# authentication (in addition to the standard none).
@@ -189,7 +216,11 @@ class socksocket(socket.socket):
elif chosenauth[1:2] == chr(0x02).encode():
# Okay, we need to perform a basic username/password
# authentication.
- self.sendall(chr(0x01).encode() + chr(len(self.__proxy[4])) + self.__proxy[4] + chr(len(self.__proxy[5])) + self.__proxy[5])
+ self.sendall(chr(0x01).encode() +
+ chr(len(self.__proxy[4])) +
+ self.__proxy[4] +
+ chr(len(self.__proxy[5])) +
+ self.__proxy[5])
authstat = self.__recvall(2)
if authstat[0:1] != chr(0x01).encode():
# Bad response
@@ -207,7 +238,7 @@ class socksocket(socket.socket):
raise Socks5AuthError((2, _socks5autherrors[2]))
else:
raise GeneralProxyError((1, _generalerrors[1]))
-
+
def __connectsocks5(self, destaddr, destport):
# Now we can request the actual connection
req = struct.pack('BBB', 0x05, 0x01, 0x00)
@@ -236,7 +267,7 @@ class socksocket(socket.socket):
elif resp[1:2] != chr(0x00).encode():
# Connection failed
self.close()
- if ord(resp[1:2])<=8:
+ if ord(resp[1:2]) <= 8:
raise Socks5Error((ord(resp[1:2]), _socks5errors[ord(resp[1:2])]))
else:
raise Socks5Error((9, _socks5errors[9]))
@@ -248,10 +279,10 @@ class socksocket(socket.socket):
boundaddr = self.__recvall(ord(resp[4:5]))
else:
self.close()
- raise GeneralProxyError((1,_generalerrors[1]))
+ raise GeneralProxyError((1, _generalerrors[1]))
boundport = struct.unpack(">H", self.__recvall(2))[0]
self.__proxysockname = (boundaddr, boundport)
- if ipaddr != None:
+ if ipaddr is not None:
self.__proxypeername = (socket.inet_ntoa(ipaddr), destport)
else:
self.__proxypeername = (destaddr, destport)
@@ -271,7 +302,7 @@ class socksocket(socket.socket):
elif resp[1:2] != chr(0x00).encode():
# Connection failed
self.close()
- if ord(resp[1:2])<=8:
+ if ord(resp[1:2]) <= 8:
raise Socks5Error((ord(resp[1:2]), _socks5errors[ord(resp[1:2])]))
else:
raise Socks5Error((9, _socks5errors[9]))
@@ -283,10 +314,10 @@ class socksocket(socket.socket):
ip = self.__recvall(ord(resp[4:5]))
else:
self.close()
- raise GeneralProxyError((1,_generalerrors[1]))
- boundport = struct.unpack(">H", self.__recvall(2))[0]
+ raise GeneralProxyError((1, _generalerrors[1]))
+ _ = struct.unpack(">H", self.__recvall(2))[0]
return ip
-
+
def getproxysockname(self):
"""getsockname() -> address info
Returns the bound IP address and port number at the proxy.
@@ -307,9 +338,10 @@ class socksocket(socket.socket):
return self.__proxypeername
def getproxytype(self):
+ """Get the proxy type"""
return self.__proxy[0]
- def __negotiatesocks4(self,destaddr,destport):
+ def __negotiatesocks4(self, destaddr, destport):
"""__negotiatesocks4(self,destaddr,destport)
Negotiates a connection through a SOCKS4 server.
"""
@@ -327,7 +359,7 @@ class socksocket(socket.socket):
# Construct the request packet
req = struct.pack(">BBH", 0x04, 0x01, destport) + ipaddr
# The username parameter is considered userid for SOCKS4
- if self.__proxy[4] != None:
+ if self.__proxy[4] is not None:
req = req + self.__proxy[4]
req = req + chr(0x00).encode()
# DNS name if remote resolving is required
@@ -341,7 +373,7 @@ class socksocket(socket.socket):
if resp[0:1] != chr(0x00).encode():
# Bad data
self.close()
- raise GeneralProxyError((1,_generalerrors[1]))
+ raise GeneralProxyError((1, _generalerrors[1]))
if resp[1:2] != chr(0x5A).encode():
# Server returned an error
self.close()
@@ -352,7 +384,7 @@ class socksocket(socket.socket):
raise Socks4Error((94, _socks4errors[4]))
# Get the bound address/port
self.__proxysockname = (socket.inet_ntoa(resp[4:]), struct.unpack(">H", resp[2:4])[0])
- if rmtrslv != None:
+ if rmtrslv is not None:
self.__proxypeername = (socket.inet_ntoa(ipaddr), destport)
else:
self.__proxypeername = (destaddr, destport)
@@ -366,7 +398,16 @@ class socksocket(socket.socket):
addr = socket.gethostbyname(destaddr)
else:
addr = destaddr
- self.sendall(("CONNECT " + addr + ":" + str(destport) + " HTTP/1.1\r\n" + "Host: " + destaddr + "\r\n\r\n").encode())
+ self.sendall(''.join([
+ "CONNECT ",
+ addr,
+ ":",
+ str(destport),
+ " HTTP/1.1\r\n",
+ "Host: ",
+ destaddr,
+ "\r\n\r\n",
+ ]).encode())
# We read the response until we get the string "\r\n\r\n"
resp = self.recv(1)
while resp.find("\r\n\r\n".encode()) == -1:
@@ -396,10 +437,15 @@ class socksocket(socket.socket):
To select the proxy server use setproxy().
"""
# Do a minimal input check first
- if (not type(destpair) in (list,tuple)) or (len(destpair) < 2) or (type(destpair[0]) != type('')) or (type(destpair[1]) != int):
+ if any([
+ not isinstance(destpair, (list, tuple)),
+ len(destpair) < 2,
+ not isinstance(destpair[0], type('')),
+ not isinstance(destpair[1], int),
+ ]):
raise GeneralProxyError((5, _generalerrors[5]))
if self.__proxy[0] == PROXY_TYPE_SOCKS5:
- if self.__proxy[2] != None:
+ if self.__proxy[2] is not None:
portnum = self.__proxy[2]
else:
portnum = 1080
@@ -419,19 +465,19 @@ class socksocket(socket.socket):
self.__negotiatesocks5()
self.__connectsocks5(destpair[0], destpair[1])
elif self.__proxy[0] == PROXY_TYPE_SOCKS4:
- if self.__proxy[2] != None:
+ if self.__proxy[2] is not None:
portnum = self.__proxy[2]
else:
portnum = 1080
- _orgsocket.connect(self,(self.__proxy[1], portnum))
+ _orgsocket.connect(self, (self.__proxy[1], portnum))
self.__negotiatesocks4(destpair[0], destpair[1])
elif self.__proxy[0] == PROXY_TYPE_HTTP:
- if self.__proxy[2] != None:
+ if self.__proxy[2] is not None:
portnum = self.__proxy[2]
else:
portnum = 8080
try:
- _orgsocket.connect(self,(self.__proxy[1], portnum))
+ _orgsocket.connect(self, (self.__proxy[1], portnum))
except socket.error as e:
# ENETUNREACH, WSAENETUNREACH
if e[0] in [101, 10051]:
@@ -444,14 +490,15 @@ class socksocket(socket.socket):
raise GeneralProxyError((9, _generalerrors[9]))
raise
self.__negotiatehttp(destpair[0], destpair[1])
- elif self.__proxy[0] == None:
+ elif self.__proxy[0] is None:
_orgsocket.connect(self, (destpair[0], destpair[1]))
else:
raise GeneralProxyError((4, _generalerrors[4]))
def resolve(self, host):
+ """TBC"""
if self.__proxy[0] == PROXY_TYPE_SOCKS5:
- if self.__proxy[2] != None:
+ if self.__proxy[2] is not None:
portnum = self.__proxy[2]
else:
portnum = 1080
diff --git a/src/upnp.py b/src/upnp.py
index 46d55956..7ea6a9a6 100644
--- a/src/upnp.py
+++ b/src/upnp.py
@@ -1,21 +1,33 @@
-# A simple upnp module to forward port for BitMessage
-# Reference: http://mattscodecave.com/posts/using-python-and-upnp-to-forward-a-port
+# pylint: disable=too-many-statements,too-many-branches,protected-access,no-self-use
+"""
+A simple upnp module to forward port for BitMessage
+Reference: http://mattscodecave.com/posts/using-python-and-upnp-to-forward-a-port
+"""
+
+from __future__ import absolute_import
+
import httplib
from random import randint
import socket
-from struct import unpack, pack
+from struct import unpack
import threading
import time
+import urllib2
+from urlparse import urlparse
+from xml.dom.minidom import Document, parseString
+
from bmconfigparser import BMConfigParser
+from debug import logger
from network.connectionpool import BMConnectionPool
-from helper_threading import *
+from helper_threading import StoppableThread
import queues
import shared
import state
import tr
+
def createRequestXML(service, action, arguments=None):
- from xml.dom.minidom import Document
+ """Router UPnP requests are XML formatted"""
doc = Document()
@@ -63,22 +75,24 @@ def createRequestXML(service, action, arguments=None):
# our tree is ready, conver it to a string
return doc.toxml()
-class UPnPError(Exception):
- def __init__(self, message):
- self.message
-class Router:
+class UPnPError(Exception):
+ """Handle a UPnP error"""
+
+ def __init__(self, message):
+ super(UPnPError, self).__init__()
+ logger.error(message)
+
+
+class Router: # pylint: disable=old-style-class
+ """TBC"""
name = ""
path = ""
address = None
routerPath = None
extPort = None
-
+
def __init__(self, ssdpResponse, address):
- import urllib2
- from xml.dom.minidom import parseString
- from urlparse import urlparse
- from debug import logger
self.address = address
@@ -92,9 +106,9 @@ class Router:
try:
self.routerPath = urlparse(header['location'])
if not self.routerPath or not hasattr(self.routerPath, "hostname"):
- logger.error ("UPnP: no hostname: %s", header['location'])
+ logger.error("UPnP: no hostname: %s", header['location'])
except KeyError:
- logger.error ("UPnP: missing location header")
+ logger.error("UPnP: missing location header")
# get the profile xml file and read it into a variable
directory = urllib2.urlopen(header['location']).read()
@@ -108,45 +122,58 @@ class Router:
for service in service_types:
if service.childNodes[0].data.find('WANIPConnection') > 0 or \
- service.childNodes[0].data.find('WANPPPConnection') > 0:
+ service.childNodes[0].data.find('WANPPPConnection') > 0:
self.path = service.parentNode.getElementsByTagName('controlURL')[0].childNodes[0].data
self.upnp_schema = service.childNodes[0].data.split(':')[-2]
- def AddPortMapping(self, externalPort, internalPort, internalClient, protocol, description, leaseDuration = 0, enabled = 1):
- from debug import logger
+ def AddPortMapping(
+ self,
+ externalPort,
+ internalPort,
+ internalClient,
+ protocol,
+ description,
+ leaseDuration=0,
+ enabled=1,
+ ): # pylint: disable=too-many-arguments
+ """Add UPnP port mapping"""
+
resp = self.soapRequest(self.upnp_schema + ':1', 'AddPortMapping', [
- ('NewRemoteHost', ''),
- ('NewExternalPort', str(externalPort)),
- ('NewProtocol', protocol),
- ('NewInternalPort', str(internalPort)),
- ('NewInternalClient', internalClient),
- ('NewEnabled', str(enabled)),
- ('NewPortMappingDescription', str(description)),
- ('NewLeaseDuration', str(leaseDuration))
- ])
+ ('NewRemoteHost', ''),
+ ('NewExternalPort', str(externalPort)),
+ ('NewProtocol', protocol),
+ ('NewInternalPort', str(internalPort)),
+ ('NewInternalClient', internalClient),
+ ('NewEnabled', str(enabled)),
+ ('NewPortMappingDescription', str(description)),
+ ('NewLeaseDuration', str(leaseDuration))
+ ])
self.extPort = externalPort
- logger.info("Successfully established UPnP mapping for %s:%i on external port %i", internalClient, internalPort, externalPort)
+ logger.info("Successfully established UPnP mapping for %s:%i on external port %i",
+ internalClient, internalPort, externalPort)
return resp
def DeletePortMapping(self, externalPort, protocol):
- from debug import logger
+ """Delete UPnP port mapping"""
+
resp = self.soapRequest(self.upnp_schema + ':1', 'DeletePortMapping', [
- ('NewRemoteHost', ''),
- ('NewExternalPort', str(externalPort)),
- ('NewProtocol', protocol),
- ])
+ ('NewRemoteHost', ''),
+ ('NewExternalPort', str(externalPort)),
+ ('NewProtocol', protocol),
+ ])
logger.info("Removed UPnP mapping on external port %i", externalPort)
return resp
def GetExternalIPAddress(self):
- from xml.dom.minidom import parseString
+ """Get the external address"""
+
resp = self.soapRequest(self.upnp_schema + ':1', 'GetExternalIPAddress')
dom = parseString(resp)
return dom.getElementsByTagName('NewExternalIPAddress')[0].childNodes[0].data
-
+
def soapRequest(self, service, action, arguments=None):
- from xml.dom.minidom import parseString
- from debug import logger
+ """Make a request to a router"""
+
conn = httplib.HTTPConnection(self.routerPath.hostname, self.routerPath.port)
conn.request(
'POST',
@@ -155,8 +182,8 @@ class Router:
{
'SOAPAction': '"urn:schemas-upnp-org:service:%s#%s"' % (service, action),
'Content-Type': 'text/xml'
- }
- )
+ }
+ )
resp = conn.getresponse()
conn.close()
if resp.status == 500:
@@ -164,21 +191,24 @@ class Router:
try:
dom = parseString(respData)
errinfo = dom.getElementsByTagName('errorDescription')
- if len(errinfo) > 0:
+ if errinfo:
logger.error("UPnP error: %s", respData)
raise UPnPError(errinfo[0].childNodes[0].data)
except:
- raise UPnPError("Unable to parse SOAP error: %s" %(respData))
+ raise UPnPError("Unable to parse SOAP error: %s" % (respData))
return resp
+
class uPnPThread(threading.Thread, StoppableThread):
+ """Start a thread to handle UPnP activity"""
+
SSDP_ADDR = "239.255.255.250"
GOOGLE_DNS = "8.8.8.8"
SSDP_PORT = 1900
SSDP_MX = 2
SSDP_ST = "urn:schemas-upnp-org:device:InternetGatewayDevice:1"
- def __init__ (self):
+ def __init__(self):
threading.Thread.__init__(self, name="uPnPThread")
try:
self.extPort = BMConfigParser().getint('bitmessagesettings', 'extport')
@@ -194,8 +224,8 @@ class uPnPThread(threading.Thread, StoppableThread):
self.initStop()
def run(self):
- from debug import logger
-
+ """Start the thread to manage UPnP activity"""
+
logger.debug("Starting UPnP thread")
logger.debug("Local IP: %s", self.localIP)
lastSent = 0
@@ -209,9 +239,11 @@ class uPnPThread(threading.Thread, StoppableThread):
if not bound:
time.sleep(1)
+ # pylint: disable=attribute-defined-outside-init
self.localPort = BMConfigParser().getint('bitmessagesettings', 'port')
+
while state.shutdown == 0 and BMConfigParser().safeGetBoolean('bitmessagesettings', 'upnp'):
- if time.time() - lastSent > self.sendSleep and len(self.routers) == 0:
+ if time.time() - lastSent > self.sendSleep and not self.routers:
try:
self.sendSearchRouter()
except:
@@ -219,7 +251,7 @@ class uPnPThread(threading.Thread, StoppableThread):
lastSent = time.time()
try:
while state.shutdown == 0 and BMConfigParser().safeGetBoolean('bitmessagesettings', 'upnp'):
- resp,(ip,port) = self.sock.recvfrom(1000)
+ resp, (ip, _) = self.sock.recvfrom(1000)
if resp is None:
continue
newRouter = Router(resp, ip)
@@ -230,14 +262,16 @@ class uPnPThread(threading.Thread, StoppableThread):
logger.debug("Found UPnP router at %s", ip)
self.routers.append(newRouter)
self.createPortMapping(newRouter)
- 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 %1'
+ ).arg(str(self.extPort))))
# retry connections so that the submitted port is refreshed
with shared.alreadyAttemptedConnectionsListLock:
shared.alreadyAttemptedConnectionsList.clear()
shared.alreadyAttemptedConnectionsListResetTime = int(
time.time())
break
- except socket.timeout as e:
+ except socket.timeout:
pass
except:
logger.error("Failure running UPnP router search.", exc_info=True)
@@ -259,22 +293,25 @@ class uPnPThread(threading.Thread, StoppableThread):
self.deletePortMapping(router)
shared.extPort = None
if deleted:
- queues.UISignalQueue.put(('updateStatusBar', tr._translate("MainWindow",'UPnP port mapping removed')))
+ queues.UISignalQueue.put(('updateStatusBar', tr._translate("MainWindow", 'UPnP port mapping removed')))
logger.debug("UPnP thread done")
def getLocalIP(self):
+ """Get the local IP of the node"""
+
s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
s.setsockopt(socket.SOL_SOCKET, socket.SO_BROADCAST, 1)
s.connect((uPnPThread.GOOGLE_DNS, 1))
return s.getsockname()[0]
def sendSearchRouter(self):
- from debug import logger
+ """Querying for UPnP services"""
+
ssdpRequest = "M-SEARCH * HTTP/1.1\r\n" + \
- "HOST: %s:%d\r\n" % (uPnPThread.SSDP_ADDR, uPnPThread.SSDP_PORT) + \
- "MAN: \"ssdp:discover\"\r\n" + \
- "MX: %d\r\n" % (uPnPThread.SSDP_MX, ) + \
- "ST: %s\r\n" % (uPnPThread.SSDP_ST, ) + "\r\n"
+ "HOST: %s:%d\r\n" % (uPnPThread.SSDP_ADDR, uPnPThread.SSDP_PORT) + \
+ "MAN: \"ssdp:discover\"\r\n" + \
+ "MX: %d\r\n" % (uPnPThread.SSDP_MX, ) + \
+ "ST: %s\r\n" % (uPnPThread.SSDP_ST, ) + "\r\n"
try:
logger.debug("Sending UPnP query")
@@ -283,19 +320,24 @@ class uPnPThread(threading.Thread, StoppableThread):
logger.exception("UPnP send query failed")
def createPortMapping(self, router):
- from debug import logger
+ """Add a port mapping"""
for i in range(50):
try:
- routerIP, = unpack('>I', socket.inet_aton(router.address))
+ _, = unpack('>I', socket.inet_aton(router.address))
localIP = self.localIP
if i == 0:
- extPort = self.localPort # try same port first
+ extPort = self.localPort # try same port first
elif i == 1 and self.extPort:
- extPort = self.extPort # try external port from last time next
+ extPort = self.extPort # try external port from last time next
else:
extPort = randint(32767, 65535)
- logger.debug("Attempt %i, requesting UPnP mapping for %s:%i on external port %i", i, localIP, self.localPort, extPort)
+ logger.debug(
+ "Attempt %i, requesting UPnP mapping for %s:%i on external port %i",
+ i,
+ localIP,
+ self.localPort,
+ extPort)
router.AddPortMapping(extPort, self.localPort, localIP, 'TCP', 'BitMessage')
shared.extPort = extPort
self.extPort = extPort
@@ -306,7 +348,5 @@ class uPnPThread(threading.Thread, StoppableThread):
logger.debug("UPnP error: ", exc_info=True)
def deletePortMapping(self, router):
+ """Delete a port mapping"""
router.DeletePortMapping(router.extPort, 'TCP')
-
-
-