Fixed: Style and lint violations for ten more of the worst violating files

This commit is contained in:
coffeedogs 2018-06-04 11:02:33 +01:00
parent 759b760977
commit 6c96c0181e
No known key found for this signature in database
GPG Key ID: 9D818C503D0B7E70
11 changed files with 1272 additions and 656 deletions

View File

@ -1,9 +1,11 @@
[pycodestyle] [pycodestyle]
max-line-length = 119 max-line-length = 119
ignore = E722,E402
[flake8] [flake8]
max-line-length = 119 max-line-length = 119
ignore = E722 ignore = E722,E402
# E402: pylint is preferred for wrong-import-position
# pylint # pylint
[MESSAGES CONTROL] [MESSAGES CONTROL]

View File

@ -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-2016 Jonathan Warren
# Copyright (c) 2012-2018 The Bitmessage developers # Copyright (c) 2012-2018 The Bitmessage developers
"""
This is not what you run to run the Bitmessage API. Instead, enable the API 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/API ) and optionally enable daemon mode
( https://bitmessage.org/wiki/Daemon ) then run bitmessagemain.py. ( https://bitmessage.org/wiki/Daemon ) then run bitmessagemain.py.
""" """
from __future__ import absolute_import
import base64 import base64
import hashlib import hashlib
import json import json
from struct import pack
import time import time
from binascii import hexlify, unhexlify from binascii import hexlify, unhexlify
from SimpleXMLRPCServer import SimpleXMLRPCRequestHandler, SimpleXMLRPCServer from SimpleXMLRPCServer import SimpleXMLRPCRequestHandler, SimpleXMLRPCServer
from struct import pack
import shared import shared
from addresses import ( from addresses import (
@ -43,6 +48,8 @@ str_chan = '[chan]'
class APIError(Exception): class APIError(Exception):
"""APIError exception class"""
def __init__(self, error_number, error_message): def __init__(self, error_number, error_message):
super(APIError, self).__init__() super(APIError, self).__init__()
self.error_number = error_number self.error_number = error_number
@ -53,26 +60,34 @@ class APIError(Exception):
class StoppableXMLRPCServer(SimpleXMLRPCServer): class StoppableXMLRPCServer(SimpleXMLRPCServer):
"""A SimpleXMLRPCServer that honours state.shutdown"""
allow_reuse_address = True allow_reuse_address = True
def serve_forever(self): def serve_forever(self):
"""Start the SimpleXMLRPCServer"""
# pylint: disable=arguments-differ
while state.shutdown == 0: while state.shutdown == 0:
self.handle_request() 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): 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): def do_POST(self):
# Handles the HTTP POST request. """
# Attempts to interpret all HTTP POST requests as XML-RPC calls, Handles the HTTP POST request.
# which are forwarded to the server's _dispatch method for handling.
# Note: this method is the same as in SimpleXMLRPCRequestHandler, Attempts to interpret all HTTP POST requests as XML-RPC calls,
# just hacked to handle cookies 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 # Check that the path is legal
if not self.is_rpc_path_valid(): if not self.is_rpc_path_valid():
@ -98,7 +113,7 @@ class MySimpleXMLRPCRequestHandler(SimpleXMLRPCRequestHandler):
# SimpleXMLRPCDispatcher. To maintain backwards compatibility, # SimpleXMLRPCDispatcher. To maintain backwards compatibility,
# check to see if a subclass implements _dispatch and dispatch # check to see if a subclass implements _dispatch and dispatch
# using that method if present. # using that method if present.
response = self.server._marshaled_dispatch( response = self.server._marshaled_dispatch( # pylint: disable=protected-access
data, getattr(self, '_dispatch', None) data, getattr(self, '_dispatch', None)
) )
except: # This should only happen if the module is buggy except: # This should only happen if the module is buggy
@ -125,22 +140,21 @@ class MySimpleXMLRPCRequestHandler(SimpleXMLRPCRequestHandler):
self.connection.shutdown(1) self.connection.shutdown(1)
def APIAuthenticateClient(self): def APIAuthenticateClient(self):
"""Predicate to check for valid API credentials in the request header"""
if 'Authorization' in self.headers: if 'Authorization' in self.headers:
# handle Basic authentication # handle Basic authentication
enctype, encstr = self.headers.get('Authorization').split() _, encstr = self.headers.get('Authorization').split()
emailid, password = encstr.decode('base64').split(':') emailid, password = encstr.decode('base64').split(':')
return ( return (
emailid == emailid == BMConfigParser().get('bitmessagesettings', 'apiusername') and
BMConfigParser().get('bitmessagesettings', 'apiusername') password == BMConfigParser().get('bitmessagesettings', 'apipassword')
and password ==
BMConfigParser().get('bitmessagesettings', 'apipassword')
) )
else: else:
logger.warning( logger.warning(
'Authentication failed because header lacks' 'Authentication failed because header lacks'
' Authentication field') ' Authentication field')
time.sleep(2) time.sleep(2)
return False
return False return False
@ -155,6 +169,7 @@ class MySimpleXMLRPCRequestHandler(SimpleXMLRPCRequestHandler):
22, "Decode error - %s. Had trouble while decoding string: %r" 22, "Decode error - %s. Had trouble while decoding string: %r"
% (e, text) % (e, text)
) )
return None
def _verifyAddress(self, address): def _verifyAddress(self, address):
status, addressVersionNumber, streamNumber, ripe = \ status, addressVersionNumber, streamNumber, ripe = \
@ -170,15 +185,10 @@ class MySimpleXMLRPCRequestHandler(SimpleXMLRPCRequestHandler):
if status == 'invalidcharacters': if status == 'invalidcharacters':
raise APIError(9, 'Invalid characters in address: ' + address) raise APIError(9, 'Invalid characters in address: ' + address)
if status == 'versiontoohigh': if status == 'versiontoohigh':
raise APIError( raise APIError(10, 'Address version number too high (or zero) in address: ' + address)
10,
'Address version number too high (or zero) in address: '
+ address
)
if status == 'varintmalformed': if status == 'varintmalformed':
raise APIError(26, 'Malformed varint in address: ' + address) raise APIError(26, 'Malformed varint in address: ' + address)
raise APIError( raise APIError(7, 'Could not decode address: %s : %s' % (address, status))
7, 'Could not decode address: %s : %s' % (address, status))
if addressVersionNumber < 2 or addressVersionNumber > 4: if addressVersionNumber < 2 or addressVersionNumber > 4:
raise APIError( raise APIError(
11, 'The address version number currently must be 2, 3 or 4.' 11, 'The address version number currently must be 2, 3 or 4.'
@ -195,9 +205,11 @@ class MySimpleXMLRPCRequestHandler(SimpleXMLRPCRequestHandler):
# Request Handlers # Request Handlers
def HandleListAddresses(self, method): def HandleListAddresses(self, method):
"""Handle a request to list addresses"""
data = '{"addresses":[' data = '{"addresses":['
for addressInKeysFile in BMConfigParser().addresses(): for addressInKeysFile in BMConfigParser().addresses():
status, addressVersionNumber, streamNumber, hash01 = decodeAddress( status, addressVersionNumber, streamNumber, hash01 = decodeAddress( # pylint: disable=unused-variable
addressInKeysFile) addressInKeysFile)
if len(data) > 20: if len(data) > 20:
data += ',' data += ','
@ -215,11 +227,13 @@ class MySimpleXMLRPCRequestHandler(SimpleXMLRPCRequestHandler):
'enabled': 'enabled':
BMConfigParser().getboolean(addressInKeysFile, 'enabled'), BMConfigParser().getboolean(addressInKeysFile, 'enabled'),
'chan': chan 'chan': chan
}, indent=4, separators=(',', ': ')) }, indent=4, separators=(',', ': '))
data += ']}' data += ']}'
return data return data
def HandleListAddressBookEntries(self, params): def HandleListAddressBookEntries(self, params):
"""Handle a request to list address book entries"""
if len(params) == 1: if len(params) == 1:
label, = params label, = params
label = self._decode(label, "base64") label = self._decode(label, "base64")
@ -243,6 +257,8 @@ class MySimpleXMLRPCRequestHandler(SimpleXMLRPCRequestHandler):
return data return data
def HandleAddAddressBookEntry(self, params): def HandleAddAddressBookEntry(self, params):
"""Handle a request to add an address book entry"""
if len(params) != 2: if len(params) != 2:
raise APIError(0, "I need label and address") raise APIError(0, "I need label and address")
address, label = params address, label = params
@ -262,6 +278,8 @@ class MySimpleXMLRPCRequestHandler(SimpleXMLRPCRequestHandler):
return "Added address %s to address book" % address return "Added address %s to address book" % address
def HandleDeleteAddressBookEntry(self, params): def HandleDeleteAddressBookEntry(self, params):
"""Handle a request to delete an address book entry"""
if len(params) != 1: if len(params) != 1:
raise APIError(0, "I need an address") raise APIError(0, "I need an address")
address, = params address, = params
@ -274,8 +292,11 @@ class MySimpleXMLRPCRequestHandler(SimpleXMLRPCRequestHandler):
return "Deleted address book entry for %s if it existed" % address return "Deleted address book entry for %s if it existed" % address
def HandleCreateRandomAddress(self, params): 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!') raise APIError(0, 'I need parameters!')
elif len(params) == 1: elif len(params) == 1:
label, = params label, = params
eighteenByteRipe = False eighteenByteRipe = False
@ -292,19 +313,16 @@ class MySimpleXMLRPCRequestHandler(SimpleXMLRPCRequestHandler):
elif len(params) == 3: elif len(params) == 3:
label, eighteenByteRipe, totalDifficulty = params label, eighteenByteRipe, totalDifficulty = params
nonceTrialsPerByte = int( nonceTrialsPerByte = int(
defaults.networkDefaultProofOfWorkNonceTrialsPerByte defaults.networkDefaultProofOfWorkNonceTrialsPerByte * totalDifficulty)
* totalDifficulty)
payloadLengthExtraBytes = BMConfigParser().get( payloadLengthExtraBytes = BMConfigParser().get(
'bitmessagesettings', 'defaultpayloadlengthextrabytes') 'bitmessagesettings', 'defaultpayloadlengthextrabytes')
elif len(params) == 4: elif len(params) == 4:
label, eighteenByteRipe, totalDifficulty, \ label, eighteenByteRipe, totalDifficulty, \
smallMessageDifficulty = params smallMessageDifficulty = params
nonceTrialsPerByte = int( nonceTrialsPerByte = int(
defaults.networkDefaultProofOfWorkNonceTrialsPerByte defaults.networkDefaultProofOfWorkNonceTrialsPerByte * totalDifficulty)
* totalDifficulty)
payloadLengthExtraBytes = int( payloadLengthExtraBytes = int(
defaults.networkDefaultPayloadLengthExtraBytes defaults.networkDefaultPayloadLengthExtraBytes * smallMessageDifficulty)
* smallMessageDifficulty)
else: else:
raise APIError(0, 'Too many parameters!') raise APIError(0, 'Too many parameters!')
label = self._decode(label, "base64") label = self._decode(label, "base64")
@ -321,8 +339,11 @@ class MySimpleXMLRPCRequestHandler(SimpleXMLRPCRequestHandler):
return queues.apiAddressGeneratorReturnQueue.get() return queues.apiAddressGeneratorReturnQueue.get()
def HandleCreateDeterministicAddresses(self, params): 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!') raise APIError(0, 'I need parameters!')
elif len(params) == 1: elif len(params) == 1:
passphrase, = params passphrase, = params
numberOfAddresses = 1 numberOfAddresses = 1
@ -333,6 +354,7 @@ class MySimpleXMLRPCRequestHandler(SimpleXMLRPCRequestHandler):
'bitmessagesettings', 'defaultnoncetrialsperbyte') 'bitmessagesettings', 'defaultnoncetrialsperbyte')
payloadLengthExtraBytes = BMConfigParser().get( payloadLengthExtraBytes = BMConfigParser().get(
'bitmessagesettings', 'defaultpayloadlengthextrabytes') 'bitmessagesettings', 'defaultpayloadlengthextrabytes')
elif len(params) == 2: elif len(params) == 2:
passphrase, numberOfAddresses = params passphrase, numberOfAddresses = params
addressVersionNumber = 0 addressVersionNumber = 0
@ -342,6 +364,7 @@ class MySimpleXMLRPCRequestHandler(SimpleXMLRPCRequestHandler):
'bitmessagesettings', 'defaultnoncetrialsperbyte') 'bitmessagesettings', 'defaultnoncetrialsperbyte')
payloadLengthExtraBytes = BMConfigParser().get( payloadLengthExtraBytes = BMConfigParser().get(
'bitmessagesettings', 'defaultpayloadlengthextrabytes') 'bitmessagesettings', 'defaultpayloadlengthextrabytes')
elif len(params) == 3: elif len(params) == 3:
passphrase, numberOfAddresses, addressVersionNumber = params passphrase, numberOfAddresses, addressVersionNumber = params
streamNumber = 0 streamNumber = 0
@ -350,6 +373,7 @@ class MySimpleXMLRPCRequestHandler(SimpleXMLRPCRequestHandler):
'bitmessagesettings', 'defaultnoncetrialsperbyte') 'bitmessagesettings', 'defaultnoncetrialsperbyte')
payloadLengthExtraBytes = BMConfigParser().get( payloadLengthExtraBytes = BMConfigParser().get(
'bitmessagesettings', 'defaultpayloadlengthextrabytes') 'bitmessagesettings', 'defaultpayloadlengthextrabytes')
elif len(params) == 4: elif len(params) == 4:
passphrase, numberOfAddresses, addressVersionNumber, \ passphrase, numberOfAddresses, addressVersionNumber, \
streamNumber = params streamNumber = params
@ -358,6 +382,7 @@ class MySimpleXMLRPCRequestHandler(SimpleXMLRPCRequestHandler):
'bitmessagesettings', 'defaultnoncetrialsperbyte') 'bitmessagesettings', 'defaultnoncetrialsperbyte')
payloadLengthExtraBytes = BMConfigParser().get( payloadLengthExtraBytes = BMConfigParser().get(
'bitmessagesettings', 'defaultpayloadlengthextrabytes') 'bitmessagesettings', 'defaultpayloadlengthextrabytes')
elif len(params) == 5: elif len(params) == 5:
passphrase, numberOfAddresses, addressVersionNumber, \ passphrase, numberOfAddresses, addressVersionNumber, \
streamNumber, eighteenByteRipe = params streamNumber, eighteenByteRipe = params
@ -365,27 +390,26 @@ class MySimpleXMLRPCRequestHandler(SimpleXMLRPCRequestHandler):
'bitmessagesettings', 'defaultnoncetrialsperbyte') 'bitmessagesettings', 'defaultnoncetrialsperbyte')
payloadLengthExtraBytes = BMConfigParser().get( payloadLengthExtraBytes = BMConfigParser().get(
'bitmessagesettings', 'defaultpayloadlengthextrabytes') 'bitmessagesettings', 'defaultpayloadlengthextrabytes')
elif len(params) == 6: elif len(params) == 6:
passphrase, numberOfAddresses, addressVersionNumber, \ passphrase, numberOfAddresses, addressVersionNumber, \
streamNumber, eighteenByteRipe, totalDifficulty = params streamNumber, eighteenByteRipe, totalDifficulty = params
nonceTrialsPerByte = int( nonceTrialsPerByte = int(
defaults.networkDefaultProofOfWorkNonceTrialsPerByte defaults.networkDefaultProofOfWorkNonceTrialsPerByte * totalDifficulty)
* totalDifficulty)
payloadLengthExtraBytes = BMConfigParser().get( payloadLengthExtraBytes = BMConfigParser().get(
'bitmessagesettings', 'defaultpayloadlengthextrabytes') 'bitmessagesettings', 'defaultpayloadlengthextrabytes')
elif len(params) == 7: elif len(params) == 7:
passphrase, numberOfAddresses, addressVersionNumber, \ passphrase, numberOfAddresses, addressVersionNumber, \
streamNumber, eighteenByteRipe, totalDifficulty, \ streamNumber, eighteenByteRipe, totalDifficulty, \
smallMessageDifficulty = params smallMessageDifficulty = params
nonceTrialsPerByte = int( nonceTrialsPerByte = int(
defaults.networkDefaultProofOfWorkNonceTrialsPerByte defaults.networkDefaultProofOfWorkNonceTrialsPerByte * totalDifficulty)
* totalDifficulty)
payloadLengthExtraBytes = int( payloadLengthExtraBytes = int(
defaults.networkDefaultPayloadLengthExtraBytes defaults.networkDefaultPayloadLengthExtraBytes * smallMessageDifficulty)
* smallMessageDifficulty)
else: else:
raise APIError(0, 'Too many parameters!') raise APIError(0, 'Too many parameters!')
if len(passphrase) == 0: if not passphrase:
raise APIError(1, 'The specified passphrase is blank.') raise APIError(1, 'The specified passphrase is blank.')
if not isinstance(eighteenByteRipe, bool): if not isinstance(eighteenByteRipe, bool):
raise APIError( raise APIError(
@ -436,12 +460,14 @@ class MySimpleXMLRPCRequestHandler(SimpleXMLRPCRequestHandler):
return data return data
def HandleGetDeterministicAddress(self, params): def HandleGetDeterministicAddress(self, params):
"""Handle a request to get a deterministic address"""
if len(params) != 3: if len(params) != 3:
raise APIError(0, 'I need exactly 3 parameters.') raise APIError(0, 'I need exactly 3 parameters.')
passphrase, addressVersionNumber, streamNumber = params passphrase, addressVersionNumber, streamNumber = params
numberOfAddresses = 1 numberOfAddresses = 1
eighteenByteRipe = False eighteenByteRipe = False
if len(passphrase) == 0: if not passphrase:
raise APIError(1, 'The specified passphrase is blank.') raise APIError(1, 'The specified passphrase is blank.')
passphrase = self._decode(passphrase, "base64") passphrase = self._decode(passphrase, "base64")
if addressVersionNumber != 3 and addressVersionNumber != 4: if addressVersionNumber != 3 and addressVersionNumber != 4:
@ -463,12 +489,16 @@ class MySimpleXMLRPCRequestHandler(SimpleXMLRPCRequestHandler):
return queues.apiAddressGeneratorReturnQueue.get() return queues.apiAddressGeneratorReturnQueue.get()
def HandleCreateChan(self, params): def HandleCreateChan(self, params):
if len(params) == 0: """Handle a request to create a chan"""
if not params:
raise APIError(0, 'I need parameters.') raise APIError(0, 'I need parameters.')
elif len(params) == 1: elif len(params) == 1:
passphrase, = params passphrase, = params
passphrase = self._decode(passphrase, "base64") passphrase = self._decode(passphrase, "base64")
if len(passphrase) == 0:
if not passphrase:
raise APIError(1, 'The specified passphrase is blank.') raise APIError(1, 'The specified passphrase is blank.')
# It would be nice to make the label the passphrase but it is # It would be nice to make the label the passphrase but it is
# possible that the passphrase contains non-utf-8 characters. # possible that the passphrase contains non-utf-8 characters.
@ -488,18 +518,20 @@ class MySimpleXMLRPCRequestHandler(SimpleXMLRPCRequestHandler):
passphrase, True passphrase, True
)) ))
queueReturn = queues.apiAddressGeneratorReturnQueue.get() queueReturn = queues.apiAddressGeneratorReturnQueue.get()
if len(queueReturn) == 0: if not queueReturn:
raise APIError(24, 'Chan address is already present.') raise APIError(24, 'Chan address is already present.')
address = queueReturn[0] address = queueReturn[0]
return address return address
def HandleJoinChan(self, params): def HandleJoinChan(self, params):
"""Handle a request to join a chan"""
if len(params) < 2: if len(params) < 2:
raise APIError(0, 'I need two parameters.') raise APIError(0, 'I need two parameters.')
elif len(params) == 2: elif len(params) == 2:
passphrase, suppliedAddress = params passphrase, suppliedAddress = params
passphrase = self._decode(passphrase, "base64") passphrase = self._decode(passphrase, "base64")
if len(passphrase) == 0: if not passphrase:
raise APIError(1, 'The specified passphrase is blank.') raise APIError(1, 'The specified passphrase is blank.')
# It would be nice to make the label the passphrase but it is # It would be nice to make the label the passphrase but it is
# possible that the passphrase contains non-utf-8 characters. # possible that the passphrase contains non-utf-8 characters.
@ -509,8 +541,8 @@ class MySimpleXMLRPCRequestHandler(SimpleXMLRPCRequestHandler):
except: except:
label = str_chan + ' ' + repr(passphrase) label = str_chan + ' ' + repr(passphrase)
status, addressVersionNumber, streamNumber, toRipe = \ status, addressVersionNumber, streamNumber, toRipe = self._verifyAddress( # pylint: disable=unused-variable
self._verifyAddress(suppliedAddress) suppliedAddress)
suppliedAddress = addBMIfNotPresent(suppliedAddress) suppliedAddress = addBMIfNotPresent(suppliedAddress)
queues.apiAddressGeneratorReturnQueue.queue.clear() queues.apiAddressGeneratorReturnQueue.queue.clear()
queues.addressGeneratorQueue.put(( queues.addressGeneratorQueue.put((
@ -522,20 +554,19 @@ class MySimpleXMLRPCRequestHandler(SimpleXMLRPCRequestHandler):
if addressGeneratorReturnValue[0] == \ if addressGeneratorReturnValue[0] == \
'chan name does not match address': 'chan name does not match address':
raise APIError(18, '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.') 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" return "success"
def HandleLeaveChan(self, params): def HandleLeaveChan(self, params):
if len(params) == 0: """Handle a request to leave a chan"""
if not params:
raise APIError(0, 'I need parameters.') raise APIError(0, 'I need parameters.')
elif len(params) == 1: elif len(params) == 1:
address, = params address, = params
status, addressVersionNumber, streamNumber, toRipe = \ # pylint: disable=unused-variable
self._verifyAddress(address) status, addressVersionNumber, streamNumber, toRipe = self._verifyAddress(address)
address = addBMIfNotPresent(address) address = addBMIfNotPresent(address)
if not BMConfigParser().has_section(address): if not BMConfigParser().has_section(address):
raise APIError( raise APIError(
@ -550,12 +581,14 @@ class MySimpleXMLRPCRequestHandler(SimpleXMLRPCRequestHandler):
return 'success' return 'success'
def HandleDeleteAddress(self, params): def HandleDeleteAddress(self, params):
if len(params) == 0: """Handle a request to delete an address"""
if not params:
raise APIError(0, 'I need parameters.') raise APIError(0, 'I need parameters.')
elif len(params) == 1: elif len(params) == 1:
address, = params address, = params
status, addressVersionNumber, streamNumber, toRipe = \ # pylint: disable=unused-variable
self._verifyAddress(address) status, addressVersionNumber, streamNumber, toRipe = self._verifyAddress(address)
address = addBMIfNotPresent(address) address = addBMIfNotPresent(address)
if not BMConfigParser().has_section(address): if not BMConfigParser().has_section(address):
raise APIError( raise APIError(
@ -568,7 +601,9 @@ class MySimpleXMLRPCRequestHandler(SimpleXMLRPCRequestHandler):
shared.reloadMyAddressHashes() shared.reloadMyAddressHashes()
return 'success' return 'success'
def HandleGetAllInboxMessages(self, params): def HandleGetAllInboxMessages(self, params): # pylint: disable=unused-argument
"""Handle a request to get all inbox messages"""
queryreturn = sqlQuery( queryreturn = sqlQuery(
"SELECT msgid, toaddress, fromaddress, subject, received, message," "SELECT msgid, toaddress, fromaddress, subject, received, message,"
" encodingtype, read FROM inbox where folder='inbox'" " encodingtype, read FROM inbox where folder='inbox'"
@ -594,7 +629,9 @@ class MySimpleXMLRPCRequestHandler(SimpleXMLRPCRequestHandler):
data += ']}' data += ']}'
return 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( queryreturn = sqlQuery(
"SELECT msgid FROM inbox where folder='inbox' ORDER BY received") "SELECT msgid FROM inbox where folder='inbox' ORDER BY received")
data = '{"inboxMessageIds":[' data = '{"inboxMessageIds":['
@ -608,7 +645,9 @@ class MySimpleXMLRPCRequestHandler(SimpleXMLRPCRequestHandler):
return data return data
def HandleGetInboxMessageById(self, params): 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!') raise APIError(0, 'I need parameters!')
elif len(params) == 1: elif len(params) == 1:
msgid = self._decode(params[0], "hex") msgid = self._decode(params[0], "hex")
@ -649,7 +688,9 @@ class MySimpleXMLRPCRequestHandler(SimpleXMLRPCRequestHandler):
data += ']}' data += ']}'
return 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( queryreturn = sqlQuery(
"SELECT msgid, toaddress, fromaddress, subject, lastactiontime," "SELECT msgid, toaddress, fromaddress, subject, lastactiontime,"
" message, encodingtype, status, ackdata FROM sent" " message, encodingtype, status, ackdata FROM sent"
@ -676,7 +717,9 @@ class MySimpleXMLRPCRequestHandler(SimpleXMLRPCRequestHandler):
data += ']}' data += ']}'
return 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( queryreturn = sqlQuery(
"SELECT msgid FROM sent where folder='sent'" "SELECT msgid FROM sent where folder='sent'"
" ORDER BY lastactiontime" " ORDER BY lastactiontime"
@ -692,7 +735,9 @@ class MySimpleXMLRPCRequestHandler(SimpleXMLRPCRequestHandler):
return data return data
def HandleInboxMessagesByReceiver(self, params): 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!') raise APIError(0, 'I need parameters!')
toAddress = params[0] toAddress = params[0]
queryreturn = sqlQuery( queryreturn = sqlQuery(
@ -719,7 +764,9 @@ class MySimpleXMLRPCRequestHandler(SimpleXMLRPCRequestHandler):
return data return data
def HandleGetSentMessageById(self, params): 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!') raise APIError(0, 'I need parameters!')
msgid = self._decode(params[0], "hex") msgid = self._decode(params[0], "hex")
queryreturn = sqlQuery( queryreturn = sqlQuery(
@ -747,7 +794,9 @@ class MySimpleXMLRPCRequestHandler(SimpleXMLRPCRequestHandler):
return data return data
def HandleGetSentMessagesByAddress(self, params): 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!') raise APIError(0, 'I need parameters!')
fromAddress = params[0] fromAddress = params[0]
queryreturn = sqlQuery( queryreturn = sqlQuery(
@ -759,7 +808,7 @@ class MySimpleXMLRPCRequestHandler(SimpleXMLRPCRequestHandler):
data = '{"sentMessages":[' data = '{"sentMessages":['
for row in queryreturn: for row in queryreturn:
msgid, toAddress, fromAddress, subject, lastactiontime, message, \ msgid, toAddress, fromAddress, subject, lastactiontime, message, \
encodingtype, status, ackdata = row encodingtype, status, ackdata = row # pylint: disable=unused-variable
subject = shared.fixPotentiallyInvalidUTF8Data(subject) subject = shared.fixPotentiallyInvalidUTF8Data(subject)
message = shared.fixPotentiallyInvalidUTF8Data(message) message = shared.fixPotentiallyInvalidUTF8Data(message)
if len(data) > 25: if len(data) > 25:
@ -778,7 +827,9 @@ class MySimpleXMLRPCRequestHandler(SimpleXMLRPCRequestHandler):
return data return data
def HandleGetSentMessagesByAckData(self, params): 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!') raise APIError(0, 'I need parameters!')
ackData = self._decode(params[0], "hex") ackData = self._decode(params[0], "hex")
queryreturn = sqlQuery( queryreturn = sqlQuery(
@ -806,7 +857,9 @@ class MySimpleXMLRPCRequestHandler(SimpleXMLRPCRequestHandler):
return data return data
def HandleTrashMessage(self, params): 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!') raise APIError(0, 'I need parameters!')
msgid = self._decode(params[0], "hex") msgid = self._decode(params[0], "hex")
@ -817,32 +870,42 @@ class MySimpleXMLRPCRequestHandler(SimpleXMLRPCRequestHandler):
return 'Trashed message (assuming message existed).' return 'Trashed message (assuming message existed).'
def HandleTrashInboxMessage(self, params): 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!') raise APIError(0, 'I need parameters!')
msgid = self._decode(params[0], "hex") msgid = self._decode(params[0], "hex")
helper_inbox.trash(msgid) helper_inbox.trash(msgid)
return 'Trashed inbox message (assuming message existed).' return 'Trashed inbox message (assuming message existed).'
def HandleTrashSentMessage(self, params): 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!') raise APIError(0, 'I need parameters!')
msgid = self._decode(params[0], "hex") msgid = self._decode(params[0], "hex")
sqlExecute('''UPDATE sent SET folder='trash' WHERE msgid=?''', msgid) sqlExecute('''UPDATE sent SET folder='trash' WHERE msgid=?''', msgid)
return 'Trashed sent message (assuming message existed).' return 'Trashed sent message (assuming message existed).'
def HandleSendMessage(self, params): def HandleSendMessage(self, params):
if len(params) == 0: """Handle a request to send a message"""
if not params:
raise APIError(0, 'I need parameters!') raise APIError(0, 'I need parameters!')
elif len(params) == 4: elif len(params) == 4:
toAddress, fromAddress, subject, message = params toAddress, fromAddress, subject, message = params
encodingType = 2 encodingType = 2
TTL = 4 * 24 * 60 * 60 TTL = 4 * 24 * 60 * 60
elif len(params) == 5: elif len(params) == 5:
toAddress, fromAddress, subject, message, encodingType = params toAddress, fromAddress, subject, message, encodingType = params
TTL = 4 * 24 * 60 * 60 TTL = 4 * 24 * 60 * 60
elif len(params) == 6: elif len(params) == 6:
toAddress, fromAddress, subject, message, encodingType, TTL = \ toAddress, fromAddress, subject, message, encodingType, TTL = \
params params
if encodingType not in [2, 3]: if encodingType not in [2, 3]:
raise APIError(6, 'The encoding type must be 2 or 3.') raise APIError(6, 'The encoding type must be 2 or 3.')
subject = self._decode(subject, "base64") subject = self._decode(subject, "base64")
@ -855,6 +918,7 @@ class MySimpleXMLRPCRequestHandler(SimpleXMLRPCRequestHandler):
TTL = 28 * 24 * 60 * 60 TTL = 28 * 24 * 60 * 60
toAddress = addBMIfNotPresent(toAddress) toAddress = addBMIfNotPresent(toAddress)
fromAddress = addBMIfNotPresent(fromAddress) fromAddress = addBMIfNotPresent(fromAddress)
# pylint: disable=unused-variable
status, addressVersionNumber, streamNumber, toRipe = \ status, addressVersionNumber, streamNumber, toRipe = \
self._verifyAddress(toAddress) self._verifyAddress(toAddress)
self._verifyAddress(fromAddress) self._verifyAddress(fromAddress)
@ -894,7 +958,6 @@ class MySimpleXMLRPCRequestHandler(SimpleXMLRPCRequestHandler):
if queryreturn != []: if queryreturn != []:
for row in queryreturn: for row in queryreturn:
toLabel, = row toLabel, = row
# apiSignalQueue.put(('displayNewSentMessage',(toAddress,toLabel,fromAddress,subject,message,ackdata)))
queues.UISignalQueue.put(('displayNewSentMessage', ( queues.UISignalQueue.put(('displayNewSentMessage', (
toAddress, toLabel, fromAddress, subject, message, ackdata))) toAddress, toLabel, fromAddress, subject, message, ackdata)))
@ -903,19 +966,25 @@ class MySimpleXMLRPCRequestHandler(SimpleXMLRPCRequestHandler):
return hexlify(ackdata) return hexlify(ackdata)
def HandleSendBroadcast(self, params): 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!') raise APIError(0, 'I need parameters!')
if len(params) == 3: if len(params) == 3:
fromAddress, subject, message = params fromAddress, subject, message = params
encodingType = 2 encodingType = 2
TTL = 4 * 24 * 60 * 60 TTL = 4 * 24 * 60 * 60
elif len(params) == 4: elif len(params) == 4:
fromAddress, subject, message, encodingType = params fromAddress, subject, message, encodingType = params
TTL = 4 * 24 * 60 * 60 TTL = 4 * 24 * 60 * 60
elif len(params) == 5: elif len(params) == 5:
fromAddress, subject, message, encodingType, TTL = params fromAddress, subject, message, encodingType, TTL = params
if encodingType not in [2, 3]: if encodingType not in [2, 3]:
raise APIError(6, 'The encoding type must be 2 or 3.') raise APIError(6, 'The encoding type must be 2 or 3.')
subject = self._decode(subject, "base64") subject = self._decode(subject, "base64")
message = self._decode(message, "base64") message = self._decode(message, "base64")
if len(subject + message) > (2 ** 18 - 500): if len(subject + message) > (2 ** 18 - 500):
@ -961,6 +1030,8 @@ class MySimpleXMLRPCRequestHandler(SimpleXMLRPCRequestHandler):
return hexlify(ackdata) return hexlify(ackdata)
def HandleGetStatus(self, params): def HandleGetStatus(self, params):
"""Handle a request to get the status of a sent message"""
if len(params) != 1: if len(params) != 1:
raise APIError(0, 'I need one parameter!') raise APIError(0, 'I need one parameter!')
ackdata, = params ackdata, = params
@ -977,7 +1048,9 @@ class MySimpleXMLRPCRequestHandler(SimpleXMLRPCRequestHandler):
return status return status
def HandleAddSubscription(self, params): def HandleAddSubscription(self, params):
if len(params) == 0: """Handle a request to add a subscription"""
if not params:
raise APIError(0, 'I need parameters!') raise APIError(0, 'I need parameters!')
if len(params) == 1: if len(params) == 1:
address, = params address, = params
@ -1007,6 +1080,8 @@ class MySimpleXMLRPCRequestHandler(SimpleXMLRPCRequestHandler):
return 'Added subscription.' return 'Added subscription.'
def HandleDeleteSubscription(self, params): def HandleDeleteSubscription(self, params):
"""Handle a request to delete a subscription"""
if len(params) != 1: if len(params) != 1:
raise APIError(0, 'I need 1 parameter!') raise APIError(0, 'I need 1 parameter!')
address, = params address, = params
@ -1017,7 +1092,10 @@ class MySimpleXMLRPCRequestHandler(SimpleXMLRPCRequestHandler):
queues.UISignalQueue.put(('rerenderSubscriptions', '')) queues.UISignalQueue.put(('rerenderSubscriptions', ''))
return 'Deleted subscription if it existed.' 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( queryreturn = sqlQuery(
"SELECT label, address, enabled FROM subscriptions") "SELECT label, address, enabled FROM subscriptions")
data = {'subscriptions': []} data = {'subscriptions': []}
@ -1032,6 +1110,8 @@ class MySimpleXMLRPCRequestHandler(SimpleXMLRPCRequestHandler):
return json.dumps(data, indent=4, separators=(',', ': ')) return json.dumps(data, indent=4, separators=(',', ': '))
def HandleDisseminatePreEncryptedMsg(self, params): def HandleDisseminatePreEncryptedMsg(self, params):
"""Handle a request to disseminate an encrypted message"""
# The device issuing this command to PyBitmessage supplies a msg # The device issuing this command to PyBitmessage supplies a msg
# object that has already been encrypted but which still needs the POW # object that has already been encrypted but which still needs the POW
# to be done. PyBitmessage accepts this msg object and sends it out # to be done. PyBitmessage accepts this msg object and sends it out
@ -1044,17 +1124,29 @@ class MySimpleXMLRPCRequestHandler(SimpleXMLRPCRequestHandler):
encryptedPayload = self._decode(encryptedPayload, "hex") encryptedPayload = self._decode(encryptedPayload, "hex")
# Let us do the POW and attach it to the front # Let us do the POW and attach it to the front
target = 2**64 / ( target = 2**64 / (
(len(encryptedPayload) + requiredPayloadLengthExtraBytes + 8) (
* requiredAverageProofOfWorkNonceTrialsPerByte) len(encryptedPayload) + requiredPayloadLengthExtraBytes + 8
) * requiredAverageProofOfWorkNonceTrialsPerByte
)
with shared.printLock: 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() powStartTime = time.time()
initialHash = hashlib.sha512(encryptedPayload).digest() initialHash = hashlib.sha512(encryptedPayload).digest()
trialValue, nonce = proofofwork.run(target, initialHash) trialValue, nonce = proofofwork.run(target, initialHash)
with shared.printLock: with shared.printLock:
print '(For msg message via API) Found proof of work', trialValue, 'Nonce:', nonce print '(For msg message via API) Found proof of work', trialValue, 'Nonce:', nonce
try: 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: except:
pass pass
encryptedPayload = pack('>Q', nonce) + encryptedPayload encryptedPayload = pack('>Q', nonce) + encryptedPayload
@ -1071,14 +1163,18 @@ class MySimpleXMLRPCRequestHandler(SimpleXMLRPCRequestHandler):
queues.invQueue.put((toStreamNumber, inventoryHash)) queues.invQueue.put((toStreamNumber, inventoryHash))
def HandleTrashSentMessageByAckDAta(self, params): 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 # 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!') raise APIError(0, 'I need parameters!')
ackdata = self._decode(params[0], "hex") ackdata = self._decode(params[0], "hex")
sqlExecute("UPDATE sent SET folder='trash' WHERE ackdata=?", ackdata) sqlExecute("UPDATE sent SET folder='trash' WHERE ackdata=?", ackdata)
return 'Trashed sent message (assuming message existed).' 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 # The device issuing this command to PyBitmessage supplies a pubkey
# object to be disseminated to the rest of the Bitmessage network. # object to be disseminated to the rest of the Bitmessage network.
# PyBitmessage accepts this pubkey object and sends it out to the rest # PyBitmessage accepts this pubkey object and sends it out to the rest
@ -1090,9 +1186,9 @@ class MySimpleXMLRPCRequestHandler(SimpleXMLRPCRequestHandler):
payload = self._decode(payload, "hex") payload = self._decode(payload, "hex")
# Let us do the POW # Let us do the POW
target = 2 ** 64 / ( target = 2 ** 64 / ((
(len(payload) + defaults.networkDefaultPayloadLengthExtraBytes len(payload) + defaults.networkDefaultPayloadLengthExtraBytes + 8
+ 8) * defaults.networkDefaultProofOfWorkNonceTrialsPerByte) ) * defaults.networkDefaultProofOfWorkNonceTrialsPerByte)
print '(For pubkey message via API) Doing proof of work...' print '(For pubkey message via API) Doing proof of work...'
initialHash = hashlib.sha512(payload).digest() initialHash = hashlib.sha512(payload).digest()
trialValue, nonce = proofofwork.run(target, initialHash) trialValue, nonce = proofofwork.run(target, initialHash)
@ -1100,18 +1196,19 @@ class MySimpleXMLRPCRequestHandler(SimpleXMLRPCRequestHandler):
payload = pack('>Q', nonce) + payload payload = pack('>Q', nonce) + payload
pubkeyReadPosition = 8 # bypass the nonce 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 '\x00\x00\x00\x00': # if this pubkey uses 8 byte time
pubkeyReadPosition += 8 pubkeyReadPosition += 8
else: else:
pubkeyReadPosition += 4 pubkeyReadPosition += 4
# pylint: disable=unused-variable
addressVersion, addressVersionLength = decodeVarint( addressVersion, addressVersionLength = decodeVarint(
payload[pubkeyReadPosition:pubkeyReadPosition+10]) payload[pubkeyReadPosition:pubkeyReadPosition + 10])
pubkeyReadPosition += addressVersionLength pubkeyReadPosition += addressVersionLength
pubkeyStreamNumber = decodeVarint( pubkeyStreamNumber = decodeVarint(
payload[pubkeyReadPosition:pubkeyReadPosition+10])[0] payload[pubkeyReadPosition:pubkeyReadPosition + 10])[0]
inventoryHash = calculateInventoryHash(payload) inventoryHash = calculateInventoryHash(payload)
objectType = 1 # TODO: support v4 pubkeys objectType = 1 # .. todo::: support v4 pubkeys
TTL = 28 * 24 * 60 * 60 TTL = 28 * 24 * 60 * 60
Inventory()[inventoryHash] = ( Inventory()[inventoryHash] = (
objectType, pubkeyStreamNumber, payload, int(time.time()) + TTL, '' objectType, pubkeyStreamNumber, payload, int(time.time()) + TTL, ''
@ -1121,6 +1218,8 @@ class MySimpleXMLRPCRequestHandler(SimpleXMLRPCRequestHandler):
queues.invQueue.put((pubkeyStreamNumber, inventoryHash)) queues.invQueue.put((pubkeyStreamNumber, inventoryHash))
def HandleGetMessageDataByDestinationHash(self, params): 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 # Method will eventually be used by a particular Android app to
# select relevant messages. Do not yet add this to the api # select relevant messages. Do not yet add this to the api
# doc. # doc.
@ -1145,8 +1244,8 @@ class MySimpleXMLRPCRequestHandler(SimpleXMLRPCRequestHandler):
readPosition = 16 # Nonce length + time length readPosition = 16 # Nonce length + time length
# Stream Number length # Stream Number length
readPosition += decodeVarint( readPosition += decodeVarint(
payload[readPosition:readPosition+10])[1] payload[readPosition:readPosition + 10])[1]
t = (payload[readPosition:readPosition+32], hash01) t = (payload[readPosition:readPosition + 32], hash01)
sql.execute("UPDATE inventory SET tag=? WHERE hash=?", *t) sql.execute("UPDATE inventory SET tag=? WHERE hash=?", *t)
queryreturn = sqlQuery( queryreturn = sqlQuery(
@ -1161,10 +1260,12 @@ class MySimpleXMLRPCRequestHandler(SimpleXMLRPCRequestHandler):
data += ']}' data += ']}'
return data return data
def HandleClientStatus(self, params): def HandleClientStatus(self, params): # pylint: disable=unused-argument
if len(network.stats.connectedHostsList()) == 0: """Handle a request to get the status of the client"""
if not network.stats.connectedHostsList():
networkStatus = 'notConnected' networkStatus = 'notConnected'
elif len(network.stats.connectedHostsList()) > 0 \ elif not network.stats.connectedHostsList() \
and not shared.clientHasReceivedIncomingConnections: and not shared.clientHasReceivedIncomingConnections:
networkStatus = 'connectedButHaveNotReceivedIncomingConnections' networkStatus = 'connectedButHaveNotReceivedIncomingConnections'
else: else:
@ -1177,9 +1278,11 @@ class MySimpleXMLRPCRequestHandler(SimpleXMLRPCRequestHandler):
'networkStatus': networkStatus, 'networkStatus': networkStatus,
'softwareName': 'PyBitmessage', 'softwareName': 'PyBitmessage',
'softwareVersion': softwareVersion 'softwareVersion': softwareVersion
}, indent=4, separators=(',', ': ')) }, indent=4, separators=(',', ': '))
def HandleDecodeAddress(self, params): def HandleDecodeAddress(self, params):
"""Handle a request to decode an address"""
# Return a meaningful decoding of an address. # Return a meaningful decoding of an address.
if len(params) != 1: if len(params) != 1:
raise APIError(0, 'I need 1 parameter!') raise APIError(0, 'I need 1 parameter!')
@ -1190,29 +1293,41 @@ class MySimpleXMLRPCRequestHandler(SimpleXMLRPCRequestHandler):
'addressVersion': addressVersion, 'addressVersion': addressVersion,
'streamNumber': streamNumber, 'streamNumber': streamNumber,
'ripe': base64.b64encode(ripe) 'ripe': base64.b64encode(ripe)
}, indent=4, separators=(',', ': ')) }, indent=4, separators=(',', ': '))
def HandleHelloWorld(self, params): def HandleHelloWorld(self, params):
"""Test two string params"""
a, b = params a, b = params
return a + '-' + b return a + '-' + b
def HandleAdd(self, params): def HandleAdd(self, params):
"""Test two numeric params"""
a, b = params a, b = params
return a + b return a + b
def HandleStatusBar(self, params): def HandleStatusBar(self, params):
"""Handle a request to update the status bar"""
message, = params message, = params
queues.UISignalQueue.put(('updateStatusBar', message)) queues.UISignalQueue.put(('updateStatusBar', message))
def HandleDeleteAndVacuum(self, params): def HandleDeleteAndVacuum(self, params):
"""Handle a request to run the deleteandvacuum stored procedure"""
if not params: if not params:
sqlStoredProcedure('deleteandvacuume') sqlStoredProcedure('deleteandvacuume')
return 'done' return 'done'
return None
def HandleShutdown(self, params): def HandleShutdown(self, params):
"""Handle a request to huutdown the client"""
if not params: if not params:
shutdown.doCleanShutdown() shutdown.doCleanShutdown()
return 'done' return 'done'
return None
handlers = {} handlers = {}
handlers['helloWorld'] = HandleHelloWorld handlers['helloWorld'] = HandleHelloWorld
@ -1279,6 +1394,7 @@ class MySimpleXMLRPCRequestHandler(SimpleXMLRPCRequestHandler):
return self.handlers[method](self, params) return self.handlers[method](self, params)
def _dispatch(self, method, params): def _dispatch(self, method, params):
# pylint: disable=attribute-defined-outside-init
self.cookies = [] self.cookies = []
validuser = self.APIAuthenticateClient() validuser = self.APIAuthenticateClient()

View File

@ -1,37 +1,40 @@
#!/usr/bin/python2.7 #!/usr/bin/python2.7
# Copyright (c) 2012-2016 Jonathan Warren # pylint: disable=no-self-use,too-many-branches,too-many-statements,too-many-locals
# Copyright (c) 2012-2018 The Bitmessage developers """
# Distributed under the MIT/X11 software license. See the accompanying bitmessagemain.py
# file COPYING or http://www.opensource.org/licenses/mit-license.php. =================
# Right now, PyBitmessage only support connecting to stream 1. It doesn't Copyright (c) 2012-2016 Jonathan Warren
# yet contain logic to expand into further streams. 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 The software version variable is now held in shared.py
import sys
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 ctypes
import errno
import getopt
import os
import signal
import socket
import sys
import threading
from random import randint
from struct import pack from struct import pack
from subprocess import call from subprocess import call
from time import sleep 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 api import MySimpleXMLRPCRequestHandler, StoppableXMLRPCServer
from helper_startup import ( from helper_startup import (
@ -39,11 +42,11 @@ from helper_startup import (
) )
import defaults import defaults
import depends
import shared import shared
import knownnodes import knownnodes
import state import state
import shutdown import shutdown
import threading
# Classes # Classes
from class_sqlThread import sqlThread from class_sqlThread import sqlThread
@ -72,7 +75,12 @@ import helper_generic
import helper_threading import helper_threading
depends.check_dependencies()
def connectToStream(streamNumber): def connectToStream(streamNumber):
"""Connect to a stream"""
state.streamsInWhichIAmParticipating.append(streamNumber) state.streamsInWhichIAmParticipating.append(streamNumber)
selfInitiatedConnections[streamNumber] = {} selfInitiatedConnections[streamNumber] = {}
@ -93,10 +101,10 @@ def connectToStream(streamNumber):
with knownnodes.knownNodesLock: with knownnodes.knownNodesLock:
if streamNumber not in knownnodes.knownNodes: if streamNumber not in knownnodes.knownNodes:
knownnodes.knownNodes[streamNumber] = {} knownnodes.knownNodes[streamNumber] = {}
if streamNumber*2 not in knownnodes.knownNodes: if streamNumber * 2 not in knownnodes.knownNodes:
knownnodes.knownNodes[streamNumber*2] = {} knownnodes.knownNodes[streamNumber * 2] = {}
if streamNumber*2+1 not in knownnodes.knownNodes: if streamNumber * 2 + 1 not in knownnodes.knownNodes:
knownnodes.knownNodes[streamNumber*2+1] = {} knownnodes.knownNodes[streamNumber * 2 + 1] = {}
BMConnectionPool().connectToStream(streamNumber) BMConnectionPool().connectToStream(streamNumber)
@ -114,6 +122,8 @@ def _fixSocket():
addressToString = ctypes.windll.ws2_32.WSAAddressToStringA addressToString = ctypes.windll.ws2_32.WSAAddressToStringA
def inet_ntop(family, host): def inet_ntop(family, host):
"""Convert IPv4 and IPv6 addresses from binary to text form"""
if family == socket.AF_INET: if family == socket.AF_INET:
if len(host) != 4: if len(host) != 4:
raise ValueError("invalid IPv4 host") raise ValueError("invalid IPv4 host")
@ -135,6 +145,8 @@ def _fixSocket():
stringToAddress = ctypes.windll.ws2_32.WSAStringToAddressA stringToAddress = ctypes.windll.ws2_32.WSAStringToAddressA
def inet_pton(family, host): def inet_pton(family, host):
"""Convert IPv4 and IPv6 addresses from text to binary form"""
buf = "\0" * 28 buf = "\0" * 28
lengthBuf = pack("I", len(buf)) lengthBuf = pack("I", len(buf))
if stringToAddress(str(host), if stringToAddress(str(host),
@ -158,13 +170,15 @@ def _fixSocket():
socket.IPV6_V6ONLY = 27 socket.IPV6_V6ONLY = 27
# This thread, of which there is only one, runs the API.
class singleAPI(threading.Thread, helper_threading.StoppableThread): class singleAPI(threading.Thread, helper_threading.StoppableThread):
"""This thread, of which there is only one, runs the API."""
def __init__(self): def __init__(self):
threading.Thread.__init__(self, name="singleAPI") threading.Thread.__init__(self, name="singleAPI")
self.initStop() self.initStop()
def stopThread(self): def stopThread(self):
"""Stop the API thread"""
super(singleAPI, self).stopThread() super(singleAPI, self).stopThread()
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM) s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
try: try:
@ -178,9 +192,10 @@ class singleAPI(threading.Thread, helper_threading.StoppableThread):
pass pass
def run(self): def run(self):
"""Run the API thread"""
port = BMConfigParser().getint('bitmessagesettings', 'apiport') port = BMConfigParser().getint('bitmessagesettings', 'apiport')
try: try:
from errno import WSAEADDRINUSE from errno import WSAEADDRINUSE # pylint: disable=unused-variable
except (ImportError, AttributeError): except (ImportError, AttributeError):
errno.WSAEADDRINUSE = errno.EADDRINUSE errno.WSAEADDRINUSE = errno.EADDRINUSE
for attempt in range(50): for attempt in range(50):
@ -215,15 +230,18 @@ if shared.useVeryEasyProofOfWorkForTesting:
defaults.networkDefaultPayloadLengthExtraBytes / 100) defaults.networkDefaultPayloadLengthExtraBytes / 100)
class Main: class Main(object):
"""The main app"""
def start(self): def start(self):
"""Start the main app"""
_fixSocket() _fixSocket()
daemon = BMConfigParser().safeGetBoolean( daemon = BMConfigParser().safeGetBoolean(
'bitmessagesettings', 'daemon') 'bitmessagesettings', 'daemon')
try: try:
opts, args = getopt.getopt( opts, _ = getopt.getopt(
sys.argv[1:], "hcdt", sys.argv[1:], "hcdt",
["help", "curses", "daemon", "test"]) ["help", "curses", "daemon", "test"])
@ -231,7 +249,7 @@ class Main:
self.usage() self.usage()
sys.exit(2) sys.exit(2)
for opt, arg in opts: for opt, _ in opts:
if opt in ("-h", "--help"): if opt in ("-h", "--help"):
self.usage() self.usage()
sys.exit() sys.exit()
@ -249,7 +267,7 @@ class Main:
if daemon and not state.testmode: if daemon and not state.testmode:
with shared.printLock: 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.daemonize()
self.setSignalHandler() self.setSignalHandler()
@ -381,8 +399,7 @@ class Main:
BMConfigParser().remove_option('bitmessagesettings', 'dontconnect') BMConfigParser().remove_option('bitmessagesettings', 'dontconnect')
elif daemon is False: elif daemon is False:
if state.curses: if state.curses:
# if depends.check_curses(): print 'Running with curses'
print('Running with curses')
import bitmessagecurses import bitmessagecurses
bitmessagecurses.runwrapper() bitmessagecurses.runwrapper()
elif depends.check_pyqt(): elif depends.check_pyqt():
@ -411,6 +428,7 @@ class Main:
sleep(1) sleep(1)
def daemonize(self): def daemonize(self):
"""Daemonise"""
grandfatherPid = os.getpid() grandfatherPid = os.getpid()
parentPid = None parentPid = None
try: try:
@ -420,7 +438,7 @@ class Main:
# wait until grandchild ready # wait until grandchild ready
while True: while True:
sleep(1) sleep(1)
os._exit(0) sys.exit(0)
except AttributeError: except AttributeError:
# fork not implemented # fork not implemented
pass pass
@ -441,7 +459,7 @@ class Main:
# wait until child ready # wait until child ready
while True: while True:
sleep(1) sleep(1)
os._exit(0) sys.exit(0)
except AttributeError: except AttributeError:
# fork not implemented # fork not implemented
pass pass
@ -463,11 +481,13 @@ class Main:
os.kill(grandfatherPid, signal.SIGTERM) os.kill(grandfatherPid, signal.SIGTERM)
def setSignalHandler(self): def setSignalHandler(self):
"""Register signal handlers"""
signal.signal(signal.SIGINT, helper_generic.signal_handler) signal.signal(signal.SIGINT, helper_generic.signal_handler)
signal.signal(signal.SIGTERM, helper_generic.signal_handler) signal.signal(signal.SIGTERM, helper_generic.signal_handler)
# signal.signal(signal.SIGINT, signal.SIG_DFL)
def usage(self): def usage(self):
"""Print usage message"""
print 'Usage: ' + sys.argv[0] + ' [OPTIONS]' print 'Usage: ' + sys.argv[0] + ' [OPTIONS]'
print ''' print '''
Options: Options:
@ -480,12 +500,18 @@ All parameters are optional.
''' '''
def stop(self): def stop(self):
"""Stop the daemon"""
with shared.printLock: with shared.printLock:
print('Stopping Bitmessage Deamon.') print 'Stopping Bitmessage Daemon.'
shutdown.doCleanShutdown() shutdown.doCleanShutdown()
# TODO: nice function but no one is using this
def getApiAddress(self): 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( if not BMConfigParser().safeGetBoolean(
'bitmessagesettings', 'apienabled'): 'bitmessagesettings', 'apienabled'):
return None return None
@ -495,14 +521,10 @@ All parameters are optional.
def main(): def main():
"""Create and start the main app"""
mainprogram = Main() mainprogram = Main()
mainprogram.start() mainprogram.start()
if __name__ == "__main__": if __name__ == "__main__":
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

View File

@ -1,20 +1,29 @@
# -*- coding: utf-8 -*- # -*- 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
# 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!
# """
# WARNING! All changes made in this file will be lost!
from __future__ import absolute_import
import sys
from PyQt4 import QtCore, QtGui from PyQt4 import QtCore, QtGui
from bmconfigparser import BMConfigParser from bmconfigparser import BMConfigParser
from foldertree import AddressBookCompleter from . import bitmessage_icons_rc # pylint: disable=unused-import
from messageview import MessageView from . import settingsmixin
from messagecompose import MessageCompose from .messageview import MessageView
import settingsmixin from .messagecompose import MessageCompose
from networkstatus import NetworkStatus from .networkstatus import NetworkStatus
from blacklist import Blacklist from .blacklist import Blacklist
from .foldertree import AddressBookCompleter
try: try:
_fromUtf8 = QtCore.QString.fromUtf8 _fromUtf8 = QtCore.QString.fromUtf8
@ -24,24 +33,38 @@ except AttributeError:
try: try:
_encoding = QtGui.QApplication.UnicodeUTF8 _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: if n is None:
return QtGui.QApplication.translate(context, text, disambig, _encoding) 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: 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: if n is None:
return QtGui.QApplication.translate(context, text, disambig) 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): class Ui_MainWindow(object):
"""Encapsulate the main UI"""
# pylint: disable=too-many-instance-attributes,too-many-statements
def setupUi(self, MainWindow): def setupUi(self, MainWindow):
"""Set up the UI"""
# pylint: disable=attribute-defined-outside-init
MainWindow.setObjectName(_fromUtf8("MainWindow")) MainWindow.setObjectName(_fromUtf8("MainWindow"))
MainWindow.resize(885, 580) MainWindow.resize(885, 580)
icon = QtGui.QIcon() 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.setWindowIcon(icon)
MainWindow.setTabShape(QtGui.QTabWidget.Rounded) MainWindow.setTabShape(QtGui.QTabWidget.Rounded)
self.centralwidget = QtGui.QWidget(MainWindow) self.centralwidget = QtGui.QWidget(MainWindow)
@ -75,7 +98,11 @@ class Ui_MainWindow(object):
self.treeWidgetYourIdentities.setObjectName(_fromUtf8("treeWidgetYourIdentities")) self.treeWidgetYourIdentities.setObjectName(_fromUtf8("treeWidgetYourIdentities"))
self.treeWidgetYourIdentities.resize(200, self.treeWidgetYourIdentities.height()) self.treeWidgetYourIdentities.resize(200, self.treeWidgetYourIdentities.height())
icon1 = QtGui.QIcon() 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.treeWidgetYourIdentities.headerItem().setIcon(0, icon1)
self.verticalSplitter_12.addWidget(self.treeWidgetYourIdentities) self.verticalSplitter_12.addWidget(self.treeWidgetYourIdentities)
self.pushButtonNewAddress = QtGui.QPushButton(self.inbox) self.pushButtonNewAddress = QtGui.QPushButton(self.inbox)
@ -175,7 +202,11 @@ class Ui_MainWindow(object):
self.tableWidgetAddressBook.resize(200, self.tableWidgetAddressBook.height()) self.tableWidgetAddressBook.resize(200, self.tableWidgetAddressBook.height())
item = QtGui.QTableWidgetItem() item = QtGui.QTableWidgetItem()
icon3 = QtGui.QIcon() 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) item.setIcon(icon3)
self.tableWidgetAddressBook.setHorizontalHeaderItem(0, item) self.tableWidgetAddressBook.setHorizontalHeaderItem(0, item)
item = QtGui.QTableWidgetItem() item = QtGui.QTableWidgetItem()
@ -376,7 +407,11 @@ class Ui_MainWindow(object):
self.treeWidgetSubscriptions.setObjectName(_fromUtf8("treeWidgetSubscriptions")) self.treeWidgetSubscriptions.setObjectName(_fromUtf8("treeWidgetSubscriptions"))
self.treeWidgetSubscriptions.resize(200, self.treeWidgetSubscriptions.height()) self.treeWidgetSubscriptions.resize(200, self.treeWidgetSubscriptions.height())
icon5 = QtGui.QIcon() 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.treeWidgetSubscriptions.headerItem().setIcon(0, icon5)
self.verticalSplitter_3.addWidget(self.treeWidgetSubscriptions) self.verticalSplitter_3.addWidget(self.treeWidgetSubscriptions)
self.pushButtonAddSubscription = QtGui.QPushButton(self.subscriptions) self.pushButtonAddSubscription = QtGui.QPushButton(self.subscriptions)
@ -455,7 +490,11 @@ class Ui_MainWindow(object):
self.horizontalSplitter_4.setCollapsible(1, False) self.horizontalSplitter_4.setCollapsible(1, False)
self.gridLayout_3.addWidget(self.horizontalSplitter_4, 0, 0, 1, 1) self.gridLayout_3.addWidget(self.horizontalSplitter_4, 0, 0, 1, 1)
icon6 = QtGui.QIcon() 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.tabWidget.addTab(self.subscriptions, icon6, _fromUtf8(""))
self.chans = QtGui.QWidget() self.chans = QtGui.QWidget()
self.chans.setObjectName(_fromUtf8("chans")) self.chans.setObjectName(_fromUtf8("chans"))
@ -475,7 +514,11 @@ class Ui_MainWindow(object):
self.treeWidgetChans.setObjectName(_fromUtf8("treeWidgetChans")) self.treeWidgetChans.setObjectName(_fromUtf8("treeWidgetChans"))
self.treeWidgetChans.resize(200, self.treeWidgetChans.height()) self.treeWidgetChans.resize(200, self.treeWidgetChans.height())
icon7 = QtGui.QIcon() 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.treeWidgetChans.headerItem().setIcon(0, icon7)
self.verticalSplitter_17.addWidget(self.treeWidgetChans) self.verticalSplitter_17.addWidget(self.treeWidgetChans)
self.pushButtonAddChan = QtGui.QPushButton(self.chans) self.pushButtonAddChan = QtGui.QPushButton(self.chans)
@ -554,7 +597,11 @@ class Ui_MainWindow(object):
self.horizontalSplitter_7.setCollapsible(1, False) self.horizontalSplitter_7.setCollapsible(1, False)
self.gridLayout_4.addWidget(self.horizontalSplitter_7, 0, 0, 1, 1) self.gridLayout_4.addWidget(self.horizontalSplitter_7, 0, 0, 1, 1)
icon8 = QtGui.QIcon() 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.tabWidget.addTab(self.chans, icon8, _fromUtf8(""))
self.blackwhitelist = Blacklist() self.blackwhitelist = Blacklist()
self.tabWidget.addTab(self.blackwhitelist, QtGui.QIcon(":/newPrefix/images/blacklist.png"), "") 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) MainWindow.setTabOrder(self.textEditMessage, self.pushButtonAddSubscription)
def updateNetworkSwitchMenuLabel(self, dontconnect=None): def updateNetworkSwitchMenuLabel(self, dontconnect=None):
"""Restore last online/offline setting"""
if dontconnect is None: if dontconnect is None:
dontconnect = BMConfigParser().safeGetBoolean( dontconnect = BMConfigParser().safeGetBoolean(
'bitmessagesettings', 'dontconnect') 'bitmessagesettings', 'dontconnect')
@ -662,6 +711,8 @@ class Ui_MainWindow(object):
) )
def retranslateUi(self, MainWindow): def retranslateUi(self, MainWindow):
"""Re-translate the UI"""
MainWindow.setWindowTitle(_translate("MainWindow", "Bitmessage", None)) MainWindow.setWindowTitle(_translate("MainWindow", "Bitmessage", None))
self.treeWidgetYourIdentities.headerItem().setText(0, _translate("MainWindow", "Identities", None)) self.treeWidgetYourIdentities.headerItem().setText(0, _translate("MainWindow", "Identities", None))
self.pushButtonNewAddress.setText(_translate("MainWindow", "New Identity", 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_3.setText(_translate("MainWindow", "Subject:", None))
self.label_2.setText(_translate("MainWindow", "From:", None)) self.label_2.setText(_translate("MainWindow", "From:", None))
self.label.setText(_translate("MainWindow", "To:", None)) self.label.setText(_translate("MainWindow", "To:", None))
#self.textEditMessage.setHtml("") # self.textEditMessage.setHtml("")
self.tabWidgetSend.setTabText(self.tabWidgetSend.indexOf(self.sendDirect), _translate("MainWindow", "Send ordinary Message", None)) self.tabWidgetSend.setTabText(
self.tabWidgetSend.indexOf(
self.sendDirect),
_translate(
"MainWindow", "Send ordinary Message", None))
self.label_8.setText(_translate("MainWindow", "From:", None)) self.label_8.setText(_translate("MainWindow", "From:", None))
self.label_7.setText(_translate("MainWindow", "Subject:", None)) self.label_7.setText(_translate("MainWindow", "Subject:", None))
#self.textEditMessageBroadcast.setHtml("") # self.textEditMessageBroadcast.setHtml("")
self.tabWidgetSend.setTabText(self.tabWidgetSend.indexOf(self.sendBroadcast), _translate("MainWindow", "Send Message to your Subscribers", None)) self.tabWidgetSend.setTabText(
self.tabWidgetSend.indexOf(
self.sendBroadcast),
_translate(
"MainWindow", "Send Message to your Subscribers", None))
self.pushButtonTTL.setText(_translate("MainWindow", "TTL:", None)) self.pushButtonTTL.setText(_translate("MainWindow", "TTL:", None))
hours = 48 hours = 48
try: try:
hours = int(BMConfigParser().getint('bitmessagesettings', 'ttl')/60/60) hours = int(BMConfigParser().getint('bitmessagesettings', 'ttl') / 60 / 60)
except: except:
pass 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.pushButtonClear.setText(_translate("MainWindow", "Clear", None))
self.pushButtonSend.setText(_translate("MainWindow", "Send", None)) self.pushButtonSend.setText(_translate("MainWindow", "Send", None))
self.tabWidget.setTabText(self.tabWidget.indexOf(self.send), _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.setText(_translate("MainWindow", "Subject", None))
item = self.tableWidgetInboxSubscriptions.horizontalHeaderItem(3) item = self.tableWidgetInboxSubscriptions.horizontalHeaderItem(3)
item.setText(_translate("MainWindow", "Received", None)) 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.treeWidgetChans.headerItem().setText(0, _translate("MainWindow", "Chans", None))
self.pushButtonAddChan.setText(_translate("MainWindow", "Add Chan", None)) self.pushButtonAddChan.setText(_translate("MainWindow", "Add Chan", None))
self.inboxSearchLineEditChans.setPlaceholderText(_translate("MainWindow", "Search", None)) self.inboxSearchLineEditChans.setPlaceholderText(_translate("MainWindow", "Search", None))
@ -744,9 +813,17 @@ class Ui_MainWindow(object):
item.setText(_translate("MainWindow", "Received", None)) item.setText(_translate("MainWindow", "Received", None))
self.tabWidget.setTabText(self.tabWidget.indexOf(self.chans), _translate("MainWindow", "Chans", None)) self.tabWidget.setTabText(self.tabWidget.indexOf(self.chans), _translate("MainWindow", "Chans", None))
self.blackwhitelist.retranslateUi() 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.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.menuFile.setTitle(_translate("MainWindow", "File", None))
self.menuSettings.setTitle(_translate("MainWindow", "Settings", None)) self.menuSettings.setTitle(_translate("MainWindow", "Settings", None))
self.menuHelp.setTitle(_translate("MainWindow", "Help", 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.actionSupport.setText(_translate("MainWindow", "Contact support", None))
self.actionAbout.setText(_translate("MainWindow", "About", None)) self.actionAbout.setText(_translate("MainWindow", "About", None))
self.actionSettings.setText(_translate("MainWindow", "Settings", 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.actionDeleteAllTrashedMessages.setText(_translate("MainWindow", "Delete all trashed messages", None))
self.actionJoinChan.setText(_translate("MainWindow", "Join / Create chan", None)) self.actionJoinChan.setText(_translate("MainWindow", "Join / Create chan", None))
import bitmessage_icons_rc
if __name__ == "__main__": 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_())

View File

@ -1,12 +1,26 @@
#!/usr/bin/env python2.7 #!/usr/bin/env python2.7
# pylint: disable=no-self-use
"""
newaddresswizard.py
===================
"""
import sys
import time
from PyQt4 import QtCore, QtGui from PyQt4 import QtCore, QtGui
class NewAddressWizardIntroPage(QtGui.QWizardPage): class NewAddressWizardIntroPage(QtGui.QWizardPage):
"""The introduction page for the new address wizard"""
def __init__(self): def __init__(self):
super(QtGui.QWizardPage, self).__init__() super(NewAddressWizardIntroPage, self).__init__()
self.setTitle("Creating a new address") 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" "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") "You can still change your mind later, and register/unregister with an email service provider.\n\n")
label.setWordWrap(True) label.setWordWrap(True)
@ -22,30 +36,35 @@ class NewAddressWizardIntroPage(QtGui.QWizardPage):
layout.addWidget(self.emailAsWell) layout.addWidget(self.emailAsWell)
layout.addWidget(self.onlyBM) layout.addWidget(self.onlyBM)
self.setLayout(layout) self.setLayout(layout)
def nextId(self): def nextId(self):
"""Page 1 or 4"""
if self.emailAsWell.isChecked(): if self.emailAsWell.isChecked():
return 4 return 4
else: return 1
return 1
class NewAddressWizardRngPassphrasePage(QtGui.QWizardPage): class NewAddressWizardRngPassphrasePage(QtGui.QWizardPage):
"""The user chose a random or passphrase-based address"""
def __init__(self): def __init__(self):
super(QtGui.QWizardPage, self).__init__() super(NewAddressWizardRngPassphrasePage, self).__init__()
self.setTitle("Random or Passphrase") self.setTitle("Random or Passphrase")
label = QtGui.QLabel("<html><head/><body><p>You may generate addresses by using either random numbers or by using a passphrase. " label = QtGui.QLabel(
"If you use a passphrase, the address is called a &quot;deterministic&quot; address. " "<html><head/><body><p>You may generate addresses by using either random numbers or by using a passphrase."
"The \'Random Number\' option is selected by default but deterministic addresses have several pros and cons:</p>" " If you use a passphrase, the address is called a &quot;deterministic&quot; address."
"<table border=0><tr><td><span style=\" font-weight:600;\">Pros:</span></td><td><span style=\" font-weight:600;\">Cons:</span></td></tr>" " The \'Random Number\' option is selected by default but deterministic addresses have several pros and"
"<tr><td>You can recreate your addresses on any computer from memory. " " cons:</p><table border=0><tr><td><span style=\" font-weight:600;\">Pros:</span></td>"
"You need-not worry about backing up your keys.dat file as long as you can remember your passphrase.</td>" " <td><span style=\" font-weight:600;\">Cons:</span></td></tr>"
"<td>You must remember (or write down) your passphrase if you expect to be able " " <tr><td>You can recreate your addresses on any computer from memory."
"to recreate your keys if they are lost. " " You need-not worry about backing up your keys.dat file as long as you can remember your passphrase.</td>"
# "You must remember the address version number and the stream number along with your passphrase. " " <td>You must remember (or write down) your passphrase if you expect to be able"
"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." " to recreate your keys if they are lost."
"</p></body></html>") # "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.</p></body></html>")
label.setWordWrap(True) label.setWordWrap(True)
self.randomAddress = QtGui.QRadioButton("Use a random number generator to make an address") self.randomAddress = QtGui.QRadioButton("Use a random number generator to make an address")
@ -59,14 +78,18 @@ class NewAddressWizardRngPassphrasePage(QtGui.QWizardPage):
self.setLayout(layout) self.setLayout(layout)
def nextId(self): def nextId(self):
"""Page 2"""
if self.randomAddress.isChecked(): if self.randomAddress.isChecked():
return 2 return 2
else: return 3
return 3
class NewAddressWizardRandomPage(QtGui.QWizardPage): class NewAddressWizardRandomPage(QtGui.QWizardPage):
"""The user chose a new random address"""
def __init__(self, addresses): def __init__(self, addresses):
super(QtGui.QWizardPage, self).__init__() super(NewAddressWizardRandomPage, self).__init__()
self.setTitle("Random") self.setTitle("Random")
label = QtGui.QLabel("Random address.") label = QtGui.QLabel("Random address.")
@ -75,10 +98,11 @@ class NewAddressWizardRandomPage(QtGui.QWizardPage):
labelLabel = QtGui.QLabel("Label (not shown to anyone except you):") labelLabel = QtGui.QLabel("Label (not shown to anyone except you):")
self.labelLineEdit = QtGui.QLineEdit() 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)") "(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" 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.radioButtonMostAvailable.setChecked(True)
self.comboBoxExisting = QtGui.QComboBox() self.comboBoxExisting = QtGui.QComboBox()
self.comboBoxExisting.setEnabled(False) self.comboBoxExisting.setEnabled(False)
@ -86,10 +110,10 @@ class NewAddressWizardRandomPage(QtGui.QWizardPage):
for address in addresses: for address in addresses:
self.comboBoxExisting.addItem(address) self.comboBoxExisting.addItem(address)
# self.comboBoxExisting.setObjectName(_fromUtf8("comboBoxExisting")) self.checkBoxEighteenByteRipe = QtGui.QCheckBox(
self.checkBoxEighteenByteRipe = QtGui.QCheckBox("Spend several minutes of extra computing time to make the address(es) 1 or 2 characters shorter") "Spend several minutes of extra computing time to make the address(es) 1 or 2 characters shorter")
layout = QtGui.QGridLayout() layout = QtGui.QGridLayout()
layout.addWidget(label, 0, 0) layout.addWidget(label, 0, 0)
layout.addWidget(labelLabel, 1, 0) layout.addWidget(labelLabel, 1, 0)
@ -100,24 +124,27 @@ class NewAddressWizardRandomPage(QtGui.QWizardPage):
layout.addWidget(self.checkBoxEighteenByteRipe, 6, 0) layout.addWidget(self.checkBoxEighteenByteRipe, 6, 0)
self.setLayout(layout) 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("label", self.labelLineEdit)
self.registerField("radioButtonMostAvailable", self.radioButtonMostAvailable) self.registerField("radioButtonMostAvailable", self.radioButtonMostAvailable)
self.registerField("radioButtonExisting", self.radioButtonExisting) self.registerField("radioButtonExisting", self.radioButtonExisting)
self.registerField("comboBoxExisting", self.comboBoxExisting) 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): def nextId(self):
"""Page 6"""
return 6 return 6
class NewAddressWizardPassphrasePage(QtGui.QWizardPage): class NewAddressWizardPassphrasePage(QtGui.QWizardPage):
"""The user chose a passphrase-based address"""
def __init__(self): def __init__(self):
super(QtGui.QWizardPage, self).__init__() super(NewAddressWizardPassphrasePage, self).__init__()
self.setTitle("Passphrase") self.setTitle("Passphrase")
label = QtGui.QLabel("Deterministric address.") label = QtGui.QLabel("Deterministric address.")
@ -126,7 +153,8 @@ class NewAddressWizardPassphrasePage(QtGui.QWizardPage):
passphraseLabel = QtGui.QLabel("Passphrase") passphraseLabel = QtGui.QLabel("Passphrase")
self.lineEditPassphrase = QtGui.QLineEdit() self.lineEditPassphrase = QtGui.QLineEdit()
self.lineEditPassphrase.setEchoMode(QtGui.QLineEdit.Password) 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") retypePassphraseLabel = QtGui.QLabel("Retype passphrase")
self.lineEditPassphraseAgain = QtGui.QLineEdit() self.lineEditPassphraseAgain = QtGui.QLineEdit()
self.lineEditPassphraseAgain.setEchoMode(QtGui.QLineEdit.Password) self.lineEditPassphraseAgain.setEchoMode(QtGui.QLineEdit.Password)
@ -135,11 +163,11 @@ class NewAddressWizardPassphrasePage(QtGui.QWizardPage):
self.spinBoxNumberOfAddressesToMake = QtGui.QSpinBox() self.spinBoxNumberOfAddressesToMake = QtGui.QSpinBox()
self.spinBoxNumberOfAddressesToMake.setMinimum(1) self.spinBoxNumberOfAddressesToMake.setMinimum(1)
self.spinBoxNumberOfAddressesToMake.setProperty("value", 8) self.spinBoxNumberOfAddressesToMake.setProperty("value", 8)
# self.spinBoxNumberOfAddressesToMake.setObjectName(_fromUtf8("spinBoxNumberOfAddressesToMake"))
label2 = QtGui.QLabel("In addition to your passphrase, you must remember these numbers:") label2 = QtGui.QLabel("In addition to your passphrase, you must remember these numbers:")
label3 = QtGui.QLabel("Address version number: 4") label3 = QtGui.QLabel("Address version number: 4")
label4 = QtGui.QLabel("Stream number: 1") label4 = QtGui.QLabel("Stream number: 1")
layout = QtGui.QGridLayout() layout = QtGui.QGridLayout()
layout.addWidget(label, 0, 0, 1, 4) layout.addWidget(label, 0, 0, 1, 4)
layout.addWidget(passphraseLabel, 1, 0, 1, 4) layout.addWidget(passphraseLabel, 1, 0, 1, 4)
@ -155,34 +183,39 @@ class NewAddressWizardPassphrasePage(QtGui.QWizardPage):
self.setLayout(layout) self.setLayout(layout)
def nextId(self): def nextId(self):
"""Page 6"""
return 6 return 6
class NewAddressWizardEmailProviderPage(QtGui.QWizardPage): class NewAddressWizardEmailProviderPage(QtGui.QWizardPage):
"""The user choses the email gateway address type"""
def __init__(self): def __init__(self):
super(QtGui.QWizardPage, self).__init__() super(NewAddressWizardEmailProviderPage, self).__init__()
self.setTitle("Choose email provider") self.setTitle("Choose email provider")
label = QtGui.QLabel("Currently only Mailchuck email gateway is available " label = QtGui.QLabel("Currently only Mailchuck email gateway is available "
"(@mailchuck.com email address). In the future, maybe other gateways will be available. " "(@mailchuck.com email address). In the future, maybe other gateways will be available. "
"Press Next.") "Press Next.")
label.setWordWrap(True) label.setWordWrap(True)
# self.mailchuck = QtGui.QRadioButton("Mailchuck email gateway (@mailchuck.com)")
# self.mailchuck.setChecked(True)
layout = QtGui.QVBoxLayout() layout = QtGui.QVBoxLayout()
layout.addWidget(label) layout.addWidget(label)
# layout.addWidget(self.mailchuck)
self.setLayout(layout) self.setLayout(layout)
def nextId(self): def nextId(self):
"""Page 5"""
return 5 return 5
class NewAddressWizardEmailAddressPage(QtGui.QWizardPage): class NewAddressWizardEmailAddressPage(QtGui.QWizardPage):
"""The user provides their email gateway detauils"""
def __init__(self): def __init__(self):
super(QtGui.QWizardPage, self).__init__() super(NewAddressWizardEmailAddressPage, self).__init__()
self.setTitle("Email address") self.setTitle("Email address")
label = QtGui.QLabel("Choosing an email address. Address must end with @mailchuck.com") 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.specificEmail.setChecked(True)
self.emailLineEdit = QtGui.QLineEdit() self.emailLineEdit = QtGui.QLineEdit()
self.randomEmail = QtGui.QRadioButton("Generate a random email address") 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 = QtGui.QVBoxLayout()
layout.addWidget(label) layout.addWidget(label)
@ -203,33 +237,37 @@ class NewAddressWizardEmailAddressPage(QtGui.QWizardPage):
self.setLayout(layout) self.setLayout(layout)
def nextId(self): def nextId(self):
"""Page 6"""
return 6 return 6
class NewAddressWizardWaitPage(QtGui.QWizardPage): class NewAddressWizardWaitPage(QtGui.QWizardPage):
"""Wait for the address to be generated"""
def __init__(self): def __init__(self):
super(QtGui.QWizardPage, self).__init__() super(NewAddressWizardWaitPage, self).__init__()
self.setTitle("Wait") self.setTitle("Wait")
self.label = QtGui.QLabel("Wait!") self.label = QtGui.QLabel("Wait!")
self.label.setWordWrap(True) self.label.setWordWrap(True)
self.progressBar = QtGui.QProgressBar() self.progressBar = QtGui.QProgressBar()
self.progressBar.setMinimum(0) self.progressBar.setMinimum(0)
self.progressBar.setMaximum(100) self.progressBar.setMaximum(100)
self.progressBar.setValue(0) 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 = QtGui.QVBoxLayout()
layout.addWidget(self.label) layout.addWidget(self.label)
layout.addWidget(self.progressBar) layout.addWidget(self.progressBar)
# layout.addWidget(self.emailAsWell)
# layout.addWidget(self.onlyBM)
self.setLayout(layout) self.setLayout(layout)
def update(self, i): def update(self, i):
"""
Update the progress bar
.. todo:: remove print statement?
"""
if i == 101 and self.wizard().currentId() == 6: if i == 101 and self.wizard().currentId() == 6:
self.wizard().button(QtGui.QWizard.NextButton).click() self.wizard().button(QtGui.QWizard.NextButton).click()
return return
@ -239,15 +277,17 @@ class NewAddressWizardWaitPage(QtGui.QWizardPage):
self.progressBar.setValue(i) self.progressBar.setValue(i)
if i == 50: if i == 50:
self.emit(QtCore.SIGNAL('completeChanged()')) self.emit(QtCore.SIGNAL('completeChanged()'))
def isComplete(self): def isComplete(self):
# print "val = " + str(self.progressBar.value()) """Predicate to indicate progress is complete"""
if self.progressBar.value() >= 50: if self.progressBar.value() >= 50:
return True return True
else: return False
return False
def initializePage(self): def initializePage(self):
"""Initialize the underlying QWizardPage"""
if self.field("emailAsWell").toBool(): if self.field("emailAsWell").toBool():
val = "yes/" val = "yes/"
else: else:
@ -258,19 +298,23 @@ class NewAddressWizardWaitPage(QtGui.QWizardPage):
val += "no" val += "no"
self.label.setText("Wait! " + val) self.label.setText("Wait! " + val)
# self.wizard().button(QtGui.QWizard.NextButton).setEnabled(False)
self.progressBar.setValue(0) self.progressBar.setValue(0)
self.thread = NewAddressThread() self.thread = NewAddressThread()
self.connect(self.thread, self.thread.signal, self.update) self.connect(self.thread, self.thread.signal, self.update)
self.thread.start() self.thread.start()
def nextId(self): def nextId(self):
"""Page 10"""
return 10 return 10
class NewAddressWizardConclusionPage(QtGui.QWizardPage): class NewAddressWizardConclusionPage(QtGui.QWizardPage):
"""The user is informed their address has been created"""
def __init__(self): def __init__(self):
super(QtGui.QWizardPage, self).__init__() super(NewAddressWizardConclusionPage, self).__init__()
self.setTitle("All done!") self.setTitle("All done!")
label = QtGui.QLabel("You successfully created a new address.") label = QtGui.QLabel("You successfully created a new address.")
@ -278,14 +322,17 @@ class NewAddressWizardConclusionPage(QtGui.QWizardPage):
layout = QtGui.QVBoxLayout() layout = QtGui.QVBoxLayout()
layout.addWidget(label) layout.addWidget(label)
self.setLayout(layout) self.setLayout(layout)
class Ui_NewAddressWizard(QtGui.QWizard): class Ui_NewAddressWizard(QtGui.QWizard):
"""The wizard is a collection of pages"""
def __init__(self, addresses): def __init__(self, addresses):
super(QtGui.QWizard, self).__init__() super(Ui_NewAddressWizard, self).__init__()
self.pages = {} self.pages = {}
page = NewAddressWizardIntroPage() page = NewAddressWizardIntroPage()
self.setPage(0, page) self.setPage(0, page)
self.setStartId(0) self.setStartId(0)
@ -308,45 +355,45 @@ class Ui_NewAddressWizard(QtGui.QWizard):
self.adjustSize() self.adjustSize()
self.show() self.show()
class NewAddressThread(QtCore.QThread): class NewAddressThread(QtCore.QThread):
# pylint: disable=missing-docstring
def __init__(self): def __init__(self):
QtCore.QThread.__init__(self) QtCore.QThread.__init__(self)
self.signal = QtCore.SIGNAL("signal") self.signal = QtCore.SIGNAL("signal")
def __del__(self): def __del__(self):
self.wait() self.wait()
def createDeterministic(self): def createDeterministic(self):
pass pass
def createPassphrase(self): def createPassphrase(self):
pass pass
def broadcastAddress(self): def broadcastAddress(self):
pass pass
def registerMailchuck(self): def registerMailchuck(self):
pass pass
def waitRegistration(self): def waitRegistration(self):
pass pass
def run(self): def run(self):
import time
for i in range(1, 101): 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, i)
self.emit(self.signal, 101) self.emit(self.signal, 101)
# self.terminate()
if __name__ == '__main__': if __name__ == '__main__':
import sys
app = QtGui.QApplication(sys.argv) app = QtGui.QApplication(sys.argv)
wizard = Ui_NewAddressWizard(["a", "b", "c", "d"]) wizard = Ui_NewAddressWizard(["a", "b", "c", "d"])
if (wizard.exec_()): if wizard.exec_():
print "Email: " + ("yes" if wizard.field("emailAsWell").toBool() else "no") print "Email: " + ("yes" if wizard.field("emailAsWell").toBool() else "no")
print "BM: " + ("yes" if wizard.field("onlyBM").toBool() else "no") print "BM: " + ("yes" if wizard.field("onlyBM").toBool() else "no")
else: else:

View File

@ -1,7 +1,9 @@
# -*- Mode: Python -*- # -*- Mode: Python -*-
# Id: asyncore.py,v 2.51 2000/09/07 22:29:26 rushing Exp # Id: asyncore.py,v 2.51 2000/09/07 22:29:26 rushing Exp
# Author: Sam Rushing <rushing@nightmare.com> # Author: Sam Rushing <rushing@nightmare.com>
# 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 # Copyright 1996 by Sam Rushing
# #
@ -25,7 +27,7 @@
# CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. # 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 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 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. sophisticated high-performance network servers and clients a snap.
""" """
# randomise object order for bandwidth balancing import os
import random
import select import select
import socket import socket
import sys import sys
import time import time
from threading import current_thread from threading import current_thread
import warnings import warnings
import os
import helper_random
from errno import EALREADY, EINPROGRESS, EWOULDBLOCK, ECONNRESET, EINVAL, \ from errno import EALREADY, EINPROGRESS, EWOULDBLOCK, ECONNRESET, EINVAL, \
ENOTCONN, ESHUTDOWN, EISCONN, EBADF, ECONNABORTED, EPIPE, EAGAIN, \ ENOTCONN, ESHUTDOWN, EISCONN, EBADF, ECONNABORTED, EPIPE, EAGAIN, \
ECONNREFUSED, EHOSTUNREACH, ENETUNREACH, ENOTSOCK, EINTR, ETIMEDOUT, \ ECONNREFUSED, EHOSTUNREACH, ENETUNREACH, ENOTSOCK, EINTR, ETIMEDOUT, \
EADDRINUSE, \ EADDRINUSE, \
errorcode errorcode
try: try:
from errno import WSAEWOULDBLOCK from errno import WSAEWOULDBLOCK
except (ImportError, AttributeError): except (ImportError, AttributeError):
@ -75,13 +73,16 @@ try:
except (ImportError, AttributeError): except (ImportError, AttributeError):
WSAECONNRESET = ECONNRESET WSAECONNRESET = ECONNRESET
try: try:
from errno import WSAEADDRINUSE # side-effects on Windows or cruft?
from errno import WSAEADDRINUSE # pylint: disable=unused-import
except (ImportError, AttributeError): except (ImportError, AttributeError):
WSAEADDRINUSE = EADDRINUSE WSAEADDRINUSE = EADDRINUSE
import helper_random
_DISCONNECTED = frozenset((ECONNRESET, ENOTCONN, ESHUTDOWN, ECONNABORTED, EPIPE,
EBADF, ECONNREFUSED, EHOSTUNREACH, ENETUNREACH, ETIMEDOUT, _DISCONNECTED = frozenset((
WSAECONNRESET)) ECONNRESET, ENOTCONN, ESHUTDOWN, ECONNABORTED, EPIPE, EBADF, ECONNREFUSED,
EHOSTUNREACH, ENETUNREACH, ETIMEDOUT, WSAECONNRESET))
OP_READ = 1 OP_READ = 1
OP_WRITE = 2 OP_WRITE = 2
@ -91,17 +92,21 @@ try:
except NameError: except NameError:
socket_map = {} socket_map = {}
def _strerror(err): def _strerror(err):
try: try:
return os.strerror(err) return os.strerror(err)
except (ValueError, OverflowError, NameError): except (ValueError, OverflowError, NameError):
if err in errorcode: if err in errorcode:
return errorcode[err] return errorcode[err]
return "Unknown error %s" %err return "Unknown error %s" % err
class ExitNow(Exception): 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 pass
_reraised_exceptions = (ExitNow, KeyboardInterrupt, SystemExit) _reraised_exceptions = (ExitNow, KeyboardInterrupt, SystemExit)
maxDownloadRate = 0 maxDownloadRate = 0
@ -113,7 +118,10 @@ uploadTimestamp = 0
uploadBucket = 0 uploadBucket = 0
sentBytes = 0 sentBytes = 0
def read(obj): def read(obj):
"""Read an event from the object"""
if not can_receive(): if not can_receive():
return return
try: try:
@ -123,7 +131,10 @@ def read(obj):
except: except:
obj.handle_error() obj.handle_error()
def write(obj): def write(obj):
"""Write an event to the object"""
if not can_send(): if not can_send():
return return
try: try:
@ -133,8 +144,12 @@ def write(obj):
except: except:
obj.handle_error() obj.handle_error()
def set_rates(download, upload): def set_rates(download, upload):
"""Set throttling rates"""
global maxDownloadRate, maxUploadRate, downloadBucket, uploadBucket, downloadTimestamp, uploadTimestamp global maxDownloadRate, maxUploadRate, downloadBucket, uploadBucket, downloadTimestamp, uploadTimestamp
maxDownloadRate = float(download) * 1024 maxDownloadRate = float(download) * 1024
maxUploadRate = float(upload) * 1024 maxUploadRate = float(upload) * 1024
downloadBucket = maxDownloadRate downloadBucket = maxDownloadRate
@ -142,14 +157,24 @@ def set_rates(download, upload):
downloadTimestamp = time.time() downloadTimestamp = time.time()
uploadTimestamp = time.time() uploadTimestamp = time.time()
def can_receive(): def can_receive():
"""Predicate indicating whether the download throttle is in effect"""
return maxDownloadRate == 0 or downloadBucket > 0 return maxDownloadRate == 0 or downloadBucket > 0
def can_send(): def can_send():
"""Predicate indicating whether the upload throttle is in effect"""
return maxUploadRate == 0 or uploadBucket > 0 return maxUploadRate == 0 or uploadBucket > 0
def update_received(download=0): def update_received(download=0):
"""Update the receiving throttle"""
global receivedBytes, downloadBucket, downloadTimestamp global receivedBytes, downloadBucket, downloadTimestamp
currentTimestamp = time.time() currentTimestamp = time.time()
receivedBytes += download receivedBytes += download
if maxDownloadRate > 0: if maxDownloadRate > 0:
@ -160,8 +185,12 @@ def update_received(download=0):
downloadBucket -= download downloadBucket -= download
downloadTimestamp = currentTimestamp downloadTimestamp = currentTimestamp
def update_sent(upload=0): def update_sent(upload=0):
"""Update the sending throttle"""
global sentBytes, uploadBucket, uploadTimestamp global sentBytes, uploadBucket, uploadTimestamp
currentTimestamp = time.time() currentTimestamp = time.time()
sentBytes += upload sentBytes += upload
if maxUploadRate > 0: if maxUploadRate > 0:
@ -172,7 +201,10 @@ def update_sent(upload=0):
uploadBucket -= upload uploadBucket -= upload
uploadTimestamp = currentTimestamp uploadTimestamp = currentTimestamp
def _exception(obj): def _exception(obj):
"""Handle exceptions as appropriate"""
try: try:
obj.handle_expt_event() obj.handle_expt_event()
except _reraised_exceptions: except _reraised_exceptions:
@ -180,7 +212,10 @@ def _exception(obj):
except: except:
obj.handle_error() obj.handle_error()
def readwrite(obj, flags): def readwrite(obj, flags):
"""Read and write any pending data to/from the object"""
try: try:
if flags & select.POLLIN and can_receive(): if flags & select.POLLIN and can_receive():
obj.handle_read_event() obj.handle_read_event()
@ -200,12 +235,17 @@ def readwrite(obj, flags):
except: except:
obj.handle_error() obj.handle_error()
def select_poller(timeout=0.0, map=None): def select_poller(timeout=0.0, map=None):
"""A poller which uses select(), available on most platforms.""" """A poller which uses select(), available on most platforms."""
# pylint: disable=redefined-builtin
if map is None: if map is None:
map = socket_map map = socket_map
if map: if map:
r = []; w = []; e = [] r = []
w = []
e = []
for fd, obj in list(map.items()): for fd, obj in list(map.items()):
is_r = obj.readable() is_r = obj.readable()
is_w = obj.writable() is_w = obj.writable()
@ -251,13 +291,16 @@ def select_poller(timeout=0.0, map=None):
else: else:
current_thread().stop.wait(timeout) current_thread().stop.wait(timeout)
def poll_poller(timeout=0.0, map=None): def poll_poller(timeout=0.0, map=None):
"""A poller which uses poll(), available on most UNIXen.""" """A poller which uses poll(), available on most UNIXen."""
# pylint: disable=redefined-builtin
if map is None: if map is None:
map = socket_map map = socket_map
if timeout is not None: if timeout is not None:
# timeout is in milliseconds # timeout is in milliseconds
timeout = int(timeout*1000) timeout = int(timeout * 1000)
try: try:
poll_poller.pollster poll_poller.pollster
except AttributeError: except AttributeError:
@ -301,12 +344,16 @@ def poll_poller(timeout=0.0, map=None):
else: else:
current_thread().stop.wait(timeout) current_thread().stop.wait(timeout)
# Aliases for backward compatibility # Aliases for backward compatibility
poll = select_poller poll = select_poller
poll2 = poll3 = poll_poller poll2 = poll3 = poll_poller
def epoll_poller(timeout=0.0, map=None): def epoll_poller(timeout=0.0, map=None):
"""A poller which uses epoll(), supported on Linux 2.5.44 and newer.""" """A poller which uses epoll(), supported on Linux 2.5.44 and newer."""
# pylint: disable=redefined-builtin
if map is None: if map is None:
map = socket_map map = socket_map
try: try:
@ -346,7 +393,7 @@ def epoll_poller(timeout=0.0, map=None):
if e.errno != EINTR: if e.errno != EINTR:
raise raise
r = [] r = []
except select.error, err: except select.error as err:
if err.args[0] != EINTR: if err.args[0] != EINTR:
raise raise
r = [] r = []
@ -354,12 +401,15 @@ def epoll_poller(timeout=0.0, map=None):
obj = map.get(fd) obj = map.get(fd)
if obj is None: if obj is None:
continue continue
readwrite(obj, flags) readwrite(obj, flags)
else: else:
current_thread().stop.wait(timeout) current_thread().stop.wait(timeout)
def kqueue_poller(timeout=0.0, map=None): def kqueue_poller(timeout=0.0, map=None):
"""A poller which uses kqueue(), BSD specific.""" """A poller which uses kqueue(), BSD specific."""
# pylint: disable=redefined-builtin,no-member
if map is None: if map is None:
map = socket_map map = socket_map
try: try:
@ -408,7 +458,7 @@ def kqueue_poller(timeout=0.0, map=None):
for event in events: for event in events:
fd = event.ident fd = event.ident
obj = map.get(fd) obj = map.get(fd)
if obj is None: if obj is None:
continue continue
if event.flags & select.KQ_EV_ERROR: if event.flags & select.KQ_EV_ERROR:
@ -425,13 +475,15 @@ def kqueue_poller(timeout=0.0, map=None):
current_thread().stop.wait(timeout) current_thread().stop.wait(timeout)
def loop(timeout=30.0, use_poll=False, map=None, count=None, def loop(timeout=30.0, use_poll=False, map=None, count=None, poller=None):
poller=None): """Poll in a loop, forever if count is None"""
# pylint: disable=redefined-builtin
if map is None: if map is None:
map = socket_map map = socket_map
if count is None: if count is None:
count = True count = True
# code which grants backward compatibility with "use_poll" # code which grants backward compatibility with "use_poll"
# argument which should no longer be used in favor of # argument which should no longer be used in favor of
# "poller" # "poller"
@ -460,10 +512,13 @@ def loop(timeout=30.0, use_poll=False, map=None, count=None,
break break
# then poll # then poll
poller(subtimeout, map) poller(subtimeout, map)
if type(count) is int: if isinstance(count, int):
count = count - 1 count = count - 1
class dispatcher: class dispatcher:
"""Dispatcher for socket objects"""
# pylint: disable=too-many-public-methods,too-many-instance-attributes,old-style-class
debug = False debug = False
connected = False connected = False
@ -478,6 +533,7 @@ class dispatcher:
minTx = 1500 minTx = 1500
def __init__(self, sock=None, map=None): def __init__(self, sock=None, map=None):
# pylint: disable=redefined-builtin
if map is None: if map is None:
self._map = socket_map self._map = socket_map
else: else:
@ -510,7 +566,7 @@ class dispatcher:
self.socket = None self.socket = None
def __repr__(self): def __repr__(self):
status = [self.__class__.__module__+"."+self.__class__.__name__] status = [self.__class__.__module__ + "." + self.__class__.__name__]
if self.accepting and self.addr: if self.accepting and self.addr:
status.append('listening') status.append('listening')
elif self.connected: elif self.connected:
@ -525,7 +581,9 @@ class dispatcher:
__str__ = __repr__ __str__ = __repr__
def add_channel(self, map=None): def add_channel(self, map=None):
#self.log_info('adding channel %s' % self) """Add a channel"""
# pylint: disable=redefined-builtin
if map is None: if map is None:
map = self._map map = self._map
map[self._fileno] = self map[self._fileno] = self
@ -533,11 +591,13 @@ class dispatcher:
self.poller_filter = 0 self.poller_filter = 0
def del_channel(self, map=None): def del_channel(self, map=None):
"""Delete a channel"""
# pylint: disable=redefined-builtin
fd = self._fileno fd = self._fileno
if map is None: if map is None:
map = self._map map = self._map
if fd in map: if fd in map:
#self.log_info('closing channel %d:%s' % (fd, self))
del map[fd] del map[fd]
if self._fileno: if self._fileno:
try: try:
@ -564,25 +624,29 @@ class dispatcher:
self.poller_registered = False self.poller_registered = False
def create_socket(self, family=socket.AF_INET, socket_type=socket.SOCK_STREAM): def create_socket(self, family=socket.AF_INET, socket_type=socket.SOCK_STREAM):
"""Create a socket"""
self.family_and_type = family, socket_type self.family_and_type = family, socket_type
sock = socket.socket(family, socket_type) sock = socket.socket(family, socket_type)
sock.setblocking(0) sock.setblocking(0)
self.set_socket(sock) self.set_socket(sock)
def set_socket(self, sock, map=None): def set_socket(self, sock, map=None):
"""Set socket"""
# pylint: disable=redefined-builtin
self.socket = sock self.socket = sock
## self.__dict__['socket'] = sock
self._fileno = sock.fileno() self._fileno = sock.fileno()
self.add_channel(map) self.add_channel(map)
def set_reuse_addr(self): def set_reuse_addr(self):
# try to re-use a server port if possible """try to re-use a server port if possible"""
try: try:
self.socket.setsockopt( self.socket.setsockopt(
socket.SOL_SOCKET, socket.SO_REUSEADDR, socket.SOL_SOCKET, socket.SO_REUSEADDR,
self.socket.getsockopt(socket.SOL_SOCKET, self.socket.getsockopt(socket.SOL_SOCKET,
socket.SO_REUSEADDR) | 1 socket.SO_REUSEADDR) | 1
) )
except socket.error: except socket.error:
pass pass
@ -593,11 +657,13 @@ class dispatcher:
# ================================================== # ==================================================
def readable(self): def readable(self):
"""Predicate to indicate download throttle status"""
if maxDownloadRate > 0: if maxDownloadRate > 0:
return downloadBucket > dispatcher.minTx return downloadBucket > dispatcher.minTx
return True return True
def writable(self): def writable(self):
"""Predicate to indicate upload throttle status"""
if maxUploadRate > 0: if maxUploadRate > 0:
return uploadBucket > dispatcher.minTx return uploadBucket > dispatcher.minTx
return True return True
@ -607,21 +673,24 @@ class dispatcher:
# ================================================== # ==================================================
def listen(self, num): def listen(self, num):
"""Listen on a port"""
self.accepting = True self.accepting = True
if os.name == 'nt' and num > 5: if os.name == 'nt' and num > 5:
num = 5 num = 5
return self.socket.listen(num) return self.socket.listen(num)
def bind(self, addr): def bind(self, addr):
"""Bind to an address"""
self.addr = addr self.addr = addr
return self.socket.bind(addr) return self.socket.bind(addr)
def connect(self, address): def connect(self, address):
"""Connect to an address"""
self.connected = False self.connected = False
self.connecting = True self.connecting = True
err = self.socket.connect_ex(address) err = self.socket.connect_ex(address)
if err in (EINPROGRESS, EALREADY, EWOULDBLOCK, WSAEWOULDBLOCK) \ 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 self.addr = address
return return
if err in (0, EISCONN): if err in (0, EISCONN):
@ -631,7 +700,11 @@ class dispatcher:
raise socket.error(err, errorcode[err]) raise socket.error(err, errorcode[err])
def accept(self): 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: try:
conn, addr = self.socket.accept() conn, addr = self.socket.accept()
except TypeError: except TypeError:
@ -645,6 +718,7 @@ class dispatcher:
return conn, addr return conn, addr
def send(self, data): def send(self, data):
"""Send data"""
try: try:
result = self.socket.send(data) result = self.socket.send(data)
return result return result
@ -658,6 +732,7 @@ class dispatcher:
raise raise
def recv(self, buffer_size): def recv(self, buffer_size):
"""Receive data"""
try: try:
data = self.socket.recv(buffer_size) data = self.socket.recv(buffer_size)
if not data: if not data:
@ -665,8 +740,7 @@ class dispatcher:
# a read condition, and having recv() return 0. # a read condition, and having recv() return 0.
self.handle_close() self.handle_close()
return b'' return b''
else: return data
return data
except socket.error as why: except socket.error as why:
# winsock sometimes raises ENOTCONN # winsock sometimes raises ENOTCONN
if why.args[0] in (EAGAIN, EWOULDBLOCK, WSAEWOULDBLOCK): if why.args[0] in (EAGAIN, EWOULDBLOCK, WSAEWOULDBLOCK):
@ -678,6 +752,7 @@ class dispatcher:
raise raise
def close(self): def close(self):
"""Close connection"""
self.connected = False self.connected = False
self.accepting = False self.accepting = False
self.connecting = False self.connecting = False
@ -695,10 +770,10 @@ class dispatcher:
retattr = getattr(self.socket, attr) retattr = getattr(self.socket, attr)
except AttributeError: except AttributeError:
raise AttributeError("%s instance has no attribute '%s'" raise AttributeError("%s instance has no attribute '%s'"
%(self.__class__.__name__, attr)) % (self.__class__.__name__, attr))
else: else:
msg = "%(me)s.%(attr)s is deprecated; use %(me)s.socket.%(attr)s " \ 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) warnings.warn(msg, DeprecationWarning, stacklevel=2)
return retattr return retattr
@ -707,13 +782,16 @@ class dispatcher:
# and 'log_info' is for informational, warning and error logging. # and 'log_info' is for informational, warning and error logging.
def log(self, message): def log(self, message):
"""Log a message to stderr"""
sys.stderr.write('log: %s\n' % str(message)) sys.stderr.write('log: %s\n' % str(message))
def log_info(self, message, log_type='info'): def log_info(self, message, log_type='info'):
"""Conditionally print a message"""
if log_type not in self.ignore_log_types: 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): def handle_read_event(self):
"""Handle a read event"""
if self.accepting: if self.accepting:
# accepting sockets are never connected, they "spawn" new # accepting sockets are never connected, they "spawn" new
# sockets that are connected # sockets that are connected
@ -726,6 +804,7 @@ class dispatcher:
self.handle_read() self.handle_read()
def handle_connect_event(self): def handle_connect_event(self):
"""Handle a connection event"""
err = self.socket.getsockopt(socket.SOL_SOCKET, socket.SO_ERROR) err = self.socket.getsockopt(socket.SOL_SOCKET, socket.SO_ERROR)
if err != 0: if err != 0:
raise socket.error(err, _strerror(err)) raise socket.error(err, _strerror(err))
@ -734,6 +813,7 @@ class dispatcher:
self.connecting = False self.connecting = False
def handle_write_event(self): def handle_write_event(self):
"""Handle a write event"""
if self.accepting: if self.accepting:
# Accepting sockets shouldn't get a write event. # Accepting sockets shouldn't get a write event.
# We will pretend it didn't happen. # We will pretend it didn't happen.
@ -745,6 +825,7 @@ class dispatcher:
self.handle_write() self.handle_write()
def handle_expt_event(self): def handle_expt_event(self):
"""Handle expected exceptions"""
# handle_expt_event() is called if there might be an error on the # handle_expt_event() is called if there might be an error on the
# socket, or if there is OOB data # socket, or if there is OOB data
# check for the error condition first # check for the error condition first
@ -763,7 +844,8 @@ class dispatcher:
self.handle_expt() self.handle_expt()
def handle_error(self): 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. # sometimes a user repr method will crash.
try: try:
@ -777,89 +859,110 @@ class dispatcher:
t, t,
v, v,
tbinfo tbinfo
), ),
'error' 'error'
) )
self.handle_close() 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): def handle_accept(self):
"""Handle an accept event"""
pair = self.accept() pair = self.accept()
if pair is not None: if pair is not None:
self.handle_accepted(*pair) 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): def handle_accepted(self, sock, addr):
"""Log that the subclass does not implement handle_accepted"""
sock.close() sock.close()
self.log_info('unhandled accepted event on %s' % (addr), 'warning') self.log_info('unhandled accepted event on %s' % (addr), 'warning')
def handle_close(self): def handle_close(self):
"""Log that the subclass does not implement handle_close"""
self.log_info('unhandled close event', 'warning') self.log_info('unhandled close event', 'warning')
self.close() self.close()
# ---------------------------------------------------------------------------
# adds simple buffered output capability, useful for simple clients.
# [for more sophisticated usage use asynchat.async_chat]
# ---------------------------------------------------------------------------
class dispatcher_with_send(dispatcher): 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): def __init__(self, sock=None, map=None):
# pylint: disable=redefined-builtin
dispatcher.__init__(self, sock, map) dispatcher.__init__(self, sock, map)
self.out_buffer = b'' self.out_buffer = b''
def initiate_send(self): def initiate_send(self):
"""Initiate a send"""
num_sent = 0 num_sent = 0
num_sent = dispatcher.send(self, self.out_buffer[:512]) num_sent = dispatcher.send(self, self.out_buffer[:512])
self.out_buffer = self.out_buffer[num_sent:] self.out_buffer = self.out_buffer[num_sent:]
def handle_write(self): def handle_write(self):
"""Handle a write event"""
self.initiate_send() self.initiate_send()
def writable(self): 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): def send(self, data):
"""Send data"""
if self.debug: if self.debug:
self.log_info('sending %s' % repr(data)) self.log_info('sending %s' % repr(data))
self.out_buffer = self.out_buffer + data self.out_buffer = self.out_buffer + data
self.initiate_send() self.initiate_send()
# --------------------------------------------------------------------------- # ---------------------------------------------------------------------------
# used for debugging. # used for debugging.
# --------------------------------------------------------------------------- # ---------------------------------------------------------------------------
def compact_traceback(): def compact_traceback():
"""Return a compact traceback"""
t, v, tb = sys.exc_info() t, v, tb = sys.exc_info()
tbinfo = [] tbinfo = []
if not tb: # Must have a traceback if not tb: # Must have a traceback
raise AssertionError("traceback does not exist") raise AssertionError("traceback does not exist")
while tb: while tb:
tbinfo.append(( tbinfo.append((
tb.tb_frame.f_code.co_filename, tb.tb_frame.f_code.co_filename,
tb.tb_frame.f_code.co_name, tb.tb_frame.f_code.co_name,
str(tb.tb_lineno) str(tb.tb_lineno)
)) ))
tb = tb.tb_next tb = tb.tb_next
# just to be safe # just to be safe
del tb del tb
file, function, line = tbinfo[-1] filename, function, line = tbinfo[-1]
info = ' '.join(['[%s|%s|%s]' % x for x in tbinfo]) 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): def close_all(map=None, ignore_all=False):
"""Close all connections"""
# pylint: disable=redefined-builtin
if map is None: if map is None:
map = socket_map map = socket_map
for x in list(map.values()): for x in list(map.values()):
@ -877,6 +980,7 @@ def close_all(map=None, ignore_all=False):
raise raise
map.clear() map.clear()
# Asynchronous File I/O: # Asynchronous File I/O:
# #
# After a little research (reading man pages on various unixen, and # 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... # Regardless, this is useful for pipes, and stdin/stdout...
if os.name == 'posix': if os.name == 'posix':
import fcntl import fcntl
class file_wrapper: class file_wrapper:
# Here we override just enough to make a file """
# look like a socket for the purposes of asyncore. 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
The passed fd is automatically os.dup()'d
"""
# pylint: disable=old-style-class
def __init__(self, fd): def __init__(self, fd):
self.fd = os.dup(fd) self.fd = os.dup(fd)
def recv(self, *args): def recv(self, *args):
"""Fake recv()"""
return os.read(self.fd, *args) return os.read(self.fd, *args)
def send(self, *args): def send(self, *args):
"""Fake send()"""
return os.write(self.fd, *args) return os.write(self.fd, *args)
def getsockopt(self, level, optname, buflen=None): def getsockopt(self, level, optname, buflen=None):
"""Fake getsockopt()"""
if (level == socket.SOL_SOCKET and if (level == socket.SOL_SOCKET and
optname == socket.SO_ERROR and optname == socket.SO_ERROR and
not buflen): not buflen):
return 0 return 0
raise NotImplementedError("Only asyncore specific behaviour " raise NotImplementedError("Only asyncore specific behaviour "
"implemented.") "implemented.")
@ -919,14 +1030,19 @@ if os.name == 'posix':
write = send write = send
def close(self): def close(self):
"""Fake close()"""
os.close(self.fd) os.close(self.fd)
def fileno(self): def fileno(self):
"""Fake fileno()"""
return self.fd return self.fd
class file_dispatcher(dispatcher): class file_dispatcher(dispatcher):
"""A dispatcher for file_wrapper objects"""
def __init__(self, fd, map=None): def __init__(self, fd, map=None):
# pylint: disable=redefined-builtin
dispatcher.__init__(self, None, map) dispatcher.__init__(self, None, map)
self.connected = True self.connected = True
try: try:
@ -940,6 +1056,7 @@ if os.name == 'posix':
fcntl.fcntl(fd, fcntl.F_SETFL, flags) fcntl.fcntl(fd, fcntl.F_SETFL, flags)
def set_file(self, fd): def set_file(self, fd):
"""Set file"""
self.socket = file_wrapper(fd) self.socket = file_wrapper(fd)
self._fileno = self.socket.fileno() self._fileno = self.socket.fileno()
self.add_channel() self.add_channel()

View File

@ -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 base64
import hashlib import hashlib
import random
import socket import socket
import struct import struct
import time import time
@ -12,11 +18,11 @@ import knownnodes
from network.advanceddispatcher import AdvancedDispatcher from network.advanceddispatcher import AdvancedDispatcher
from network.dandelion import Dandelion from network.dandelion import Dandelion
from network.bmobject import BMObject, BMObjectInsufficientPOWError, BMObjectInvalidDataError, \ from network.bmobject import BMObject, BMObjectInsufficientPOWError, BMObjectInvalidDataError, \
BMObjectExpiredError, BMObjectUnwantedStreamError, BMObjectInvalidError, BMObjectAlreadyHaveError BMObjectExpiredError, BMObjectUnwantedStreamError, BMObjectInvalidError, BMObjectAlreadyHaveError
import network.connectionpool import network.connectionpool
from network.node import Node from network.node import Node
from network.objectracker import ObjectTracker from network.objectracker import ObjectTracker
from network.proxy import Proxy, ProxyError, GeneralProxyError from network.proxy import ProxyError
import addresses import addresses
from queues import objectProcessorQueue, portCheckerQueue, invQueue, addrQueue from queues import objectProcessorQueue, portCheckerQueue, invQueue, addrQueue
@ -25,19 +31,27 @@ import state
import protocol import protocol
import helper_random import helper_random
class BMProtoError(ProxyError): class BMProtoError(ProxyError):
"""A Bitmessage Protocol Base Error"""
errorCodes = ("Protocol error") errorCodes = ("Protocol error")
class BMProtoInsufficientDataError(BMProtoError): class BMProtoInsufficientDataError(BMProtoError):
"""A Bitmessage Protocol Insufficient Data Error"""
errorCodes = ("Insufficient data") errorCodes = ("Insufficient data")
class BMProtoExcessiveDataError(BMProtoError): class BMProtoExcessiveDataError(BMProtoError):
"""A Bitmessage Protocol Excessive Data Error"""
errorCodes = ("Too much data") errorCodes = ("Too much data")
class BMProto(AdvancedDispatcher, ObjectTracker): class BMProto(AdvancedDispatcher, ObjectTracker):
"""A parser for the Bitmessage Protocol"""
# ~1.6 MB which is the maximum possible size of an inv message. # ~1.6 MB which is the maximum possible size of an inv message.
maxMessageSize = 1600100 maxMessageSize = 1600100
# 2**18 = 256kB is the maximum size of an object payload # 2**18 = 256kB is the maximum size of an object payload
@ -52,12 +66,15 @@ class BMProto(AdvancedDispatcher, ObjectTracker):
maxTimeOffset = 3600 maxTimeOffset = 3600
def __init__(self, address=None, sock=None): def __init__(self, address=None, sock=None):
# pylint: disable=super-init-not-called,unused-argument
AdvancedDispatcher.__init__(self, sock) AdvancedDispatcher.__init__(self, sock)
self.isOutbound = False self.isOutbound = False
# packet/connection from a local IP # packet/connection from a local IP
self.local = False self.local = False
def bm_proto_reset(self): def bm_proto_reset(self):
"""TBC"""
self.magic = None self.magic = None
self.command = None self.command = None
self.payloadLength = 0 self.payloadLength = 0
@ -69,7 +86,10 @@ class BMProto(AdvancedDispatcher, ObjectTracker):
self.object = None self.object = None
def state_bm_header(self): 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') self.command = self.command.rstrip('\x00')
if self.magic != 0xE9BEB4D9: if self.magic != 0xE9BEB4D9:
# skip 1 byte in order to sync # skip 1 byte in order to sync
@ -84,8 +104,9 @@ class BMProto(AdvancedDispatcher, ObjectTracker):
self.invalid = True self.invalid = True
self.set_state("bm_command", length=protocol.Header.size, expectBytes=self.payloadLength) self.set_state("bm_command", length=protocol.Header.size, expectBytes=self.payloadLength)
return True return True
def state_bm_command(self): def state_bm_command(self):
"""TBC"""
self.payload = self.read_buf[:self.payloadLength] self.payload = self.read_buf[:self.payloadLength]
if self.checksum != hashlib.sha512(self.payload).digest()[0:4]: if self.checksum != hashlib.sha512(self.payload).digest()[0:4]:
logger.debug("Bad checksum, ignoring") logger.debug("Bad checksum, ignoring")
@ -122,7 +143,7 @@ class BMProto(AdvancedDispatcher, ObjectTracker):
# broken read, ignore # broken read, ignore
pass pass
else: 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) logger.debug("Closing due to invalid command %s", self.command)
self.close_reason = "Invalid command %s" % (self.command) self.close_reason = "Invalid command %s" % (self.command)
self.set_state("close") self.set_state("close")
@ -134,16 +155,21 @@ class BMProto(AdvancedDispatcher, ObjectTracker):
return True return True
def decode_payload_string(self, length): 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 self.payloadOffset += length
return value return value
def decode_payload_varint(self): def decode_payload_varint(self):
"""Decode a varint from the payload"""
value, offset = addresses.decodeVarint(self.payload[self.payloadOffset:]) value, offset = addresses.decodeVarint(self.payload[self.payloadOffset:])
self.payloadOffset += offset self.payloadOffset += offset
return value return value
def decode_payload_node(self): def decode_payload_node(self):
"""Decode node details from the payload"""
services, host, port = self.decode_payload_content("Q16sH") services, host, port = self.decode_payload_content("Q16sH")
if host[0:12] == '\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xFF\xFF': 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])) host = socket.inet_ntop(socket.AF_INET, str(host[12:16]))
@ -153,38 +179,45 @@ class BMProto(AdvancedDispatcher, ObjectTracker):
else: else:
host = socket.inet_ntop(socket.AF_INET6, str(host)) host = socket.inet_ntop(socket.AF_INET6, str(host))
if host == "": if host == "":
# This can happen on Windows systems which are not 64-bit compatible # This can happen on Windows systems which are not 64-bit compatible
# so let us drop the IPv6 address. # so let us drop the IPv6 address.
host = socket.inet_ntop(socket.AF_INET, str(host[12:16])) host = socket.inet_ntop(socket.AF_INET, str(host[12:16]))
return Node(services, host, port) return Node(services, host, port)
def decode_payload_content(self, pattern = "v"): 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 Decode the payload
# v = varint (or array)
# H = uint16 L = varint indicating the length of the next array
# I = uint32 l = varint indicating the length of the next item
# Q = uint64 v = varint (or array)
# i = net_addr (without time and stream number) H = uint16
# s = string I = uint32
# 0-9 = length of the next item Q = uint64
# , = end of array 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"): def decode_simple(self, char="v"):
"""Some expected objects can be decoded very straightforwardly"""
if char == "v": if char == "v":
return self.decode_payload_varint() return self.decode_payload_varint()
if char == "i": if char == "i":
return self.decode_payload_node() return self.decode_payload_node()
if char == "H": if char == "H":
self.payloadOffset += 2 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": if char == "I":
self.payloadOffset += 4 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": if char == "Q":
self.payloadOffset += 8 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 size = None
isArray = False isArray = False
@ -197,27 +230,19 @@ class BMProto(AdvancedDispatcher, ObjectTracker):
# retval (array) # retval (array)
parserStack = [[1, 1, False, pattern, 0, []]] parserStack = [[1, 1, False, pattern, 0, []]]
#try:
# sys._getframe(200)
# logger.error("Stack depth warning, pattern: %s", pattern)
# return
#except ValueError:
# pass
while True: while True:
i = parserStack[-1][3][parserStack[-1][4]] 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: try:
size = size * 10 + int(i) size = size * 10 + int(i)
except TypeError: except TypeError:
size = int(i) size = int(i)
isArray = False isArray = False
elif i in "Ll" and size is None: elif i in "Ll" and size is None:
size = self.decode_payload_varint() size = self.decode_payload_varint()
if i == "L": isArray = bool(i == "L")
isArray = True
else:
isArray = False
elif size is not None: elif size is not None:
if isArray: if isArray:
parserStack.append([size, size, isArray, parserStack[-1][3][parserStack[-1][4]:], 0, []]) 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])): for j in range(parserStack[-1][4], len(parserStack[-1][3])):
if parserStack[-1][3][j] not in "lL0123456789": if parserStack[-1][3][j] not in "lL0123456789":
break 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 parserStack[-2][4] += len(parserStack[-1][3]) - 1
size = None size = None
continue continue
elif i == "s": 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]] parserStack[-1][5] = self.payload[self.payloadOffset:self.payloadOffset + parserStack[-1][0]]
self.payloadOffset += parserStack[-1][0] self.payloadOffset += parserStack[-1][0]
parserStack[-1][1] = 0 parserStack[-1][1] = 0
parserStack[-1][2] = True parserStack[-1][2] = True
#del parserStack[-1]
size = None size = None
elif i in "viHIQ": elif i in "viHIQ":
parserStack[-1][5].append(decode_simple(self, parserStack[-1][3][parserStack[-1][4]])) parserStack[-1][5].append(decode_simple(self, parserStack[-1][3][parserStack[-1][4]]))
size = None size = None
else: else:
size = None size = None
for depth in range(len(parserStack) - 1, -1, -1): for depth in range(len(parserStack) - 1, -1, -1):
parserStack[depth][4] += 1 parserStack[depth][4] += 1
if parserStack[depth][4] >= len(parserStack[depth][3]): if parserStack[depth][4] >= len(parserStack[depth][3]):
@ -269,16 +295,19 @@ class BMProto(AdvancedDispatcher, ObjectTracker):
raise BMProtoInsufficientDataError() raise BMProtoInsufficientDataError()
def bm_command_error(self): def bm_command_error(self):
"""TBC"""
# pylint: disable=unused-variable
fatalStatus, banTime, inventoryVector, errorText = self.decode_payload_content("vvlsls") fatalStatus, banTime, inventoryVector, errorText = self.decode_payload_content("vvlsls")
logger.error("%s:%i error: %i, %s", self.destination.host, self.destination.port, fatalStatus, errorText) logger.error("%s:%i error: %i, %s", self.destination.host, self.destination.port, fatalStatus, errorText)
return True return True
def bm_command_getdata(self): def bm_command_getdata(self):
"""TBC"""
items = self.decode_payload_content("l32s") items = self.decode_payload_content("l32s")
# skip? # ..todo:: skip?
if time.time() < self.skipUntil: if time.time() < self.skipUntil:
return True return True
#TODO make this more asynchronous # .. todo:: make this more asynchronous
helper_random.randomshuffle(items) helper_random.randomshuffle(items)
for i in map(str, items): for i in map(str, items):
if Dandelion().hasHash(i) and \ if Dandelion().hasHash(i) and \
@ -320,6 +349,7 @@ class BMProto(AdvancedDispatcher, ObjectTracker):
return True return True
def bm_command_inv(self): def bm_command_inv(self):
"""TBC"""
return self._command_inv(False) return self._command_inv(False)
def bm_command_dinv(self): def bm_command_dinv(self):
@ -329,12 +359,16 @@ class BMProto(AdvancedDispatcher, ObjectTracker):
return self._command_inv(True) return self._command_inv(True)
def bm_command_object(self): def bm_command_object(self):
"""TBC"""
objectOffset = self.payloadOffset objectOffset = self.payloadOffset
nonce, expiresTime, objectType, version, streamNumber = self.decode_payload_content("QQIvv") nonce, expiresTime, objectType, version, streamNumber = self.decode_payload_content("QQIvv")
self.object = BMObject(nonce, expiresTime, objectType, version, streamNumber, self.payload, self.payloadOffset) self.object = BMObject(nonce, expiresTime, objectType, version, streamNumber, self.payload, self.payloadOffset)
if len(self.payload) - self.payloadOffset > BMProto.maxObjectPayloadSize: 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() raise BMProtoExcessiveDataError()
try: try:
@ -347,7 +381,9 @@ class BMProto(AdvancedDispatcher, ObjectTracker):
try: try:
self.object.checkStream() self.object.checkStream()
except (BMObjectUnwantedStreamError,) as e: 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"): if not BMConfigParser().get("inventory", "acceptmismatch"):
raise e raise e
@ -366,7 +402,10 @@ class BMProto(AdvancedDispatcher, ObjectTracker):
Dandelion().removeHash(self.object.inventoryHash, "cycle detection") Dandelion().removeHash(self.object.inventoryHash, "cycle detection")
Inventory()[self.object.inventoryHash] = ( 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) self.handleReceivedObject(self.object.streamNumber, self.object.inventoryHash)
invQueue.put((self.object.streamNumber, self.object.inventoryHash, self.destination)) invQueue.put((self.object.streamNumber, self.object.inventoryHash, self.destination))
return True return True
@ -375,9 +414,10 @@ class BMProto(AdvancedDispatcher, ObjectTracker):
return self.decode_payload_content("LQIQ16sH") return self.decode_payload_content("LQIQ16sH")
def bm_command_addr(self): def bm_command_addr(self):
addresses = self._decode_addr() """TBC"""
for i in addresses: these_addresses = self._decode_addr()
seenTime, stream, services, ip, port = i for i in these_addresses:
seenTime, stream, _, ip, port = i
decodedIP = protocol.checkIPAddress(str(ip)) decodedIP = protocol.checkIPAddress(str(ip))
if stream not in state.streamsInWhichIAmParticipating: if stream not in state.streamsInWhichIAmParticipating:
continue continue
@ -402,18 +442,23 @@ class BMProto(AdvancedDispatcher, ObjectTracker):
return True return True
def bm_command_portcheck(self): def bm_command_portcheck(self):
"""TBC"""
portCheckerQueue.put(state.Peer(self.destination, self.peerNode.port)) portCheckerQueue.put(state.Peer(self.destination, self.peerNode.port))
return True return True
def bm_command_ping(self): def bm_command_ping(self):
"""TBC"""
self.append_write_buf(protocol.CreatePacket('pong')) self.append_write_buf(protocol.CreatePacket('pong'))
return True return True
def bm_command_pong(self): def bm_command_pong(self):
# nothing really """noop"""
# pylint: disable=no-self-use
return True return True
def bm_command_verack(self): def bm_command_verack(self):
"""TBC"""
self.verackReceived = True self.verackReceived = True
if self.verackSent: if self.verackSent:
if self.isSSL: if self.isSSL:
@ -424,6 +469,8 @@ class BMProto(AdvancedDispatcher, ObjectTracker):
return True return True
def bm_command_version(self): def bm_command_version(self):
"""TBC"""
self.remoteProtocolVersion, self.services, self.timestamp, self.sockNode, self.peerNode, self.nonce, \ self.remoteProtocolVersion, self.services, self.timestamp, self.sockNode, self.peerNode, self.nonce, \
self.userAgent, self.streams = self.decode_payload_content("IQQiiQlsLv") self.userAgent, self.streams = self.decode_payload_content("IQQiiQlsLv")
self.nonce = struct.pack('>Q', self.nonce) 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("my external IP: %s", self.sockNode.host)
logger.debug("remote node incoming address: %s:%i", self.destination.host, self.peerNode.port) logger.debug("remote node incoming address: %s:%i", self.destination.host, self.peerNode.port)
logger.debug("user agent: %s", self.userAgent) logger.debug("user agent: %s", self.userAgent)
logger.debug("streams: [%s]", ",".join(map(str,self.streams))) logger.debug("streams: [%s]", ",".join(map(str, self.streams)))
if not self.peerValidityChecks(): if not self.peerValidityChecks():
# TODO ABORT # .. todo:: ABORT
return True return True
#shared.connectedHostsList[self.destination] = self.streams[0]
self.append_write_buf(protocol.CreatePacket('verack')) self.append_write_buf(protocol.CreatePacket('verack'))
self.verackSent = True self.verackSent = True
if not self.isOutbound: if not self.isOutbound:
self.append_write_buf(protocol.assembleVersionMessage(self.destination.host, self.destination.port, \ self.append_write_buf(
network.connectionpool.BMConnectionPool().streams, True, nodeid=self.nodeid)) protocol.assembleVersionMessage(
#print "%s:%i: Sending version" % (self.destination.host, self.destination.port) self.destination.host,
self.destination.port,
network.connectionpool.BMConnectionPool().streams,
True,
nodeid=self.nodeid))
if ((self.services & protocol.NODE_SSL == protocol.NODE_SSL) and if ((self.services & protocol.NODE_SSL == protocol.NODE_SSL) and
protocol.haveSSL(not self.isOutbound)): protocol.haveSSL(not self.isOutbound)):
self.isSSL = True self.isSSL = True
@ -457,41 +507,45 @@ class BMProto(AdvancedDispatcher, ObjectTracker):
return True return True
def peerValidityChecks(self): def peerValidityChecks(self):
"""Check the validity of peers"""
if self.remoteProtocolVersion < 3: if self.remoteProtocolVersion < 3:
self.append_write_buf(protocol.assembleErrorMessage(fatal=2, self.append_write_buf(protocol.assembleErrorMessage(
errorText="Your is using an old protocol. Closing connection.")) fatal=2, errorText="Your is using an old protocol. Closing connection."))
logger.debug ('Closing connection to old protocol version %s, node: %s', logger.debug('Closing connection to old protocol version %s, node: %s',
str(self.remoteProtocolVersion), str(self.destination)) str(self.remoteProtocolVersion), str(self.destination))
return False return False
if self.timeOffset > BMProto.maxTimeOffset: if self.timeOffset > BMProto.maxTimeOffset:
self.append_write_buf(protocol.assembleErrorMessage(fatal=2, self.append_write_buf(
errorText="Your time is too far in the future compared to mine. Closing connection.")) 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.", 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 shared.timeOffsetWrongCount += 1
return False return False
elif self.timeOffset < -BMProto.maxTimeOffset: elif self.timeOffset < -BMProto.maxTimeOffset:
self.append_write_buf(protocol.assembleErrorMessage(fatal=2, self.append_write_buf(protocol.assembleErrorMessage(
errorText="Your time is too far in the past compared to mine. Closing connection.")) 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.", 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 shared.timeOffsetWrongCount += 1
return False return False
else: else:
shared.timeOffsetWrongCount = 0 shared.timeOffsetWrongCount = 0
if not self.streams: if not self.streams:
self.append_write_buf(protocol.assembleErrorMessage(fatal=2, self.append_write_buf(protocol.assembleErrorMessage(
errorText="We don't have shared stream interests. Closing connection.")) 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.', logger.debug('Closed connection to %s because there is no overlapping interest in streams.',
str(self.destination)) str(self.destination))
return False return False
if self.destination in network.connectionpool.BMConnectionPool().inboundConnections: if self.destination in network.connectionpool.BMConnectionPool().inboundConnections:
try: try:
if not protocol.checkSocksIP(self.destination.host): if not protocol.checkSocksIP(self.destination.host):
self.append_write_buf(protocol.assembleErrorMessage(fatal=2, self.append_write_buf(
errorText="Too many connections from your IP. Closing connection.")) protocol.assembleErrorMessage(
logger.debug ('Closed connection to %s because we are already connected to that IP.', fatal=2, errorText="Too many connections from your IP. Closing connection."))
str(self.destination)) logger.debug('Closed connection to %s because we are already connected to that IP.',
str(self.destination))
return False return False
except: except:
pass pass
@ -499,27 +553,30 @@ class BMProto(AdvancedDispatcher, ObjectTracker):
# incoming from a peer we're connected to as outbound, or server full # incoming from a peer we're connected to as outbound, or server full
# report the same error to counter deanonymisation # report the same error to counter deanonymisation
if state.Peer(self.destination.host, self.peerNode.port) in \ if state.Peer(self.destination.host, self.peerNode.port) in \
network.connectionpool.BMConnectionPool().inboundConnections or \ network.connectionpool.BMConnectionPool().inboundConnections or \
len(network.connectionpool.BMConnectionPool().inboundConnections) + \ len(network.connectionpool.BMConnectionPool().inboundConnections) + \
len(network.connectionpool.BMConnectionPool().outboundConnections) > \ len(network.connectionpool.BMConnectionPool().outboundConnections) > \
BMConfigParser().safeGetInt("bitmessagesettings", "maxtotalconnections") + \ BMConfigParser().safeGetInt("bitmessagesettings", "maxtotalconnections") + \
BMConfigParser().safeGetInt("bitmessagesettings", "maxbootstrapconnections"): BMConfigParser().safeGetInt("bitmessagesettings", "maxbootstrapconnections"):
self.append_write_buf(protocol.assembleErrorMessage(fatal=2, self.append_write_buf(protocol.assembleErrorMessage(fatal=2,
errorText="Server full, please try again later.")) errorText="Server full, please try again later."))
logger.debug ("Closed connection to %s due to server full or duplicate inbound/outbound.", logger.debug("Closed connection to %s due to server full or duplicate inbound/outbound.",
str(self.destination)) str(self.destination))
return False return False
if network.connectionpool.BMConnectionPool().isAlreadyConnected(self.nonce): if network.connectionpool.BMConnectionPool().isAlreadyConnected(self.nonce):
self.append_write_buf(protocol.assembleErrorMessage(fatal=2, self.append_write_buf(
errorText="I'm connected to myself. Closing connection.")) protocol.assembleErrorMessage(
logger.debug ("Closed connection to %s because I'm connected to myself.", fatal=2,
str(self.destination)) 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 False
return True return True
@staticmethod @staticmethod
def assembleAddr(peerList): def assembleAddr(peerList):
"""iBuild up a packed address"""
if isinstance(peerList, state.Peer): if isinstance(peerList, state.Peer):
peerList = (peerList) peerList = (peerList)
if not peerList: if not peerList:
@ -541,6 +598,7 @@ class BMProto(AdvancedDispatcher, ObjectTracker):
@staticmethod @staticmethod
def stopDownloadingObject(hashId, forwardAnyway=False): def stopDownloadingObject(hashId, forwardAnyway=False):
"""Stop downloading an object"""
for connection in network.connectionpool.BMConnectionPool().inboundConnections.values() + \ for connection in network.connectionpool.BMConnectionPool().inboundConnections.values() + \
network.connectionpool.BMConnectionPool().outboundConnections.values(): network.connectionpool.BMConnectionPool().outboundConnections.values():
try: try:
@ -559,6 +617,7 @@ class BMProto(AdvancedDispatcher, ObjectTracker):
pass pass
def handle_close(self): def handle_close(self):
"""Handle close"""
self.set_state("close") self.set_state("close")
if not (self.accepting or self.connecting or self.connected): if not (self.accepting or self.connecting or self.connected):
# already disconnected # already disconnected

View File

@ -1,41 +1,45 @@
import base64 # pylint: disable=too-many-ancestors
from binascii import hexlify """
import hashlib tcp.py
======
"""
from __future__ import absolute_import
import math import math
import time import time
from pprint import pprint
import socket import socket
import struct
import random import random
import traceback
from addresses import calculateInventoryHash
from debug import logger from debug import logger
from helper_random import randomBytes from helper_random import randomBytes
import helper_random import helper_random
from inventory import Inventory from inventory import Inventory
import knownnodes import knownnodes
from network.advanceddispatcher import AdvancedDispatcher 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 import network.connectionpool
from network.dandelion import Dandelion from network.dandelion import Dandelion
from network.node import Node
import network.asyncore_pollchoose as asyncore import network.asyncore_pollchoose as asyncore
from network.proxy import Proxy, ProxyError, GeneralProxyError
from network.objectracker import ObjectTracker from network.objectracker import ObjectTracker
from network.socks5 import Socks5Connection, Socks5Resolver, Socks5AuthError, Socks5Error from network.socks5 import Socks5Connection
from network.socks4a import Socks4aConnection, Socks4aResolver, Socks4aError from network.socks4a import Socks4aConnection
from network.tls import TLSDispatcher from network.tls import TLSDispatcher
import addresses import addresses
from bmconfigparser import BMConfigParser from bmconfigparser import BMConfigParser
from queues import invQueue, objectProcessorQueue, portCheckerQueue, UISignalQueue, receiveDataQueue from queues import invQueue, UISignalQueue, receiveDataQueue
import shared import shared
import state import state
import protocol 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): def __init__(self, address=None, sock=None):
BMProto.__init__(self, address=address, sock=sock) BMProto.__init__(self, address=address, sock=sock)
self.verackReceived = False self.verackReceived = False
@ -67,18 +71,27 @@ class TCPConnection(BMProto, TLSDispatcher):
self.connect(self.destination) self.connect(self.destination)
logger.debug("Connecting to %s:%i", self.destination.host, self.destination.port) logger.debug("Connecting to %s:%i", self.destination.host, self.destination.port)
encodedAddr = protocol.encodeHost(self.destination.host) encodedAddr = protocol.encodeHost(self.destination.host)
if protocol.checkIPAddress(encodedAddr, True) and not protocol.checkSocksIP(self.destination.host): self.local = all([
self.local = True protocol.checkIPAddress(encodedAddr, True),
else: not protocol.checkSocksIP(self.destination.host)
self.local = False ])
#shared.connectedHostsList[self.destination] = 0 ObjectTracker.__init__(self) # pylint: disable=non-parent-init-called
ObjectTracker.__init__(self)
self.bm_proto_reset() self.bm_proto_reset()
self.set_state("bm_header", expectBytes=protocol.Header.size) 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 # 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 # take the stream with maximum amount of nodes
# +2 is to avoid problems with log(0) and log(1) # +2 is to avoid problems with log(0) and log(1)
# 20 is avg connected nodes count # 20 is avg connected nodes count
@ -93,12 +106,14 @@ class TCPConnection(BMProto, TLSDispatcher):
self.skipUntil = time.time() + delay self.skipUntil = time.time() + delay
def state_connection_fully_established(self): def state_connection_fully_established(self):
"""TBC"""
self.set_connection_fully_established() self.set_connection_fully_established()
self.set_state("bm_header") self.set_state("bm_header")
self.bm_proto_reset() self.bm_proto_reset()
return True return True
def set_connection_fully_established(self): def set_connection_fully_established(self):
"""TBC"""
if not self.isOutbound and not self.local: if not self.isOutbound and not self.local:
shared.clientHasReceivedIncomingConnections = True shared.clientHasReceivedIncomingConnections = True
UISignalQueue.put(('setStatusIcon', 'green')) UISignalQueue.put(('setStatusIcon', 'green'))
@ -113,50 +128,50 @@ class TCPConnection(BMProto, TLSDispatcher):
self.sendBigInv() self.sendBigInv()
def sendAddr(self): def sendAddr(self):
"""TBC"""
# We are going to share a maximum number of 1000 addrs (per overlapping # We are going to share a maximum number of 1000 addrs (per overlapping
# stream) with our peer. 500 from overlapping streams, 250 from the # stream) with our peer. 500 from overlapping streams, 250 from the
# left child stream, and 250 from the right child stream. # left child stream, and 250 from the right child stream.
maxAddrCount = BMConfigParser().safeGetInt("bitmessagesettings", "maxaddrperstreamsend", 500) maxAddrCount = BMConfigParser().safeGetInt("bitmessagesettings", "maxaddrperstreamsend", 500)
# init # init
addressCount = 0
payload = b''
templist = [] templist = []
addrs = {} addrs = {}
for stream in self.streams: for stream in self.streams:
with knownnodes.knownNodesLock: with knownnodes.knownNodesLock:
if len(knownnodes.knownNodes[stream]) > 0: if knownnodes.knownNodes[stream]:
filtered = {k: v for k, v in knownnodes.knownNodes[stream].items() 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) elemCount = len(filtered)
if elemCount > maxAddrCount: if elemCount > maxAddrCount:
elemCount = maxAddrCount elemCount = maxAddrCount
# only if more recent than 3 hours # only if more recent than 3 hours
addrs[stream] = helper_random.randomsample(filtered.items(), elemCount) addrs[stream] = helper_random.randomsample(filtered.items(), elemCount)
# sent 250 only if the remote isn't interested in it # sent 250 only if the remote isn't interested in it
if len(knownnodes.knownNodes[stream * 2]) > 0 and stream not in self.streams: if knownnodes.knownNodes[stream * 2] and stream not in self.streams:
filtered = {k: v for k, v in knownnodes.knownNodes[stream*2].items() filtered = {k: v for k, v in knownnodes.knownNodes[stream * 2].items()
if v["lastseen"] > (int(time.time()) - shared.maximumAgeOfNodesThatIAdvertiseToOthers)} if v["lastseen"] > (int(time.time()) - shared.maximumAgeOfNodesThatIAdvertiseToOthers)}
elemCount = len(filtered) elemCount = len(filtered)
if elemCount > maxAddrCount / 2: if elemCount > maxAddrCount / 2:
elemCount = int(maxAddrCount / 2) elemCount = int(maxAddrCount / 2)
addrs[stream * 2] = helper_random.randomsample(filtered.items(), elemCount) addrs[stream * 2] = helper_random.randomsample(filtered.items(), elemCount)
if len(knownnodes.knownNodes[(stream * 2) + 1]) > 0 and stream not in self.streams: 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() filtered = {k: v for k, v in knownnodes.knownNodes[stream * 2 + 1].items()
if v["lastseen"] > (int(time.time()) - shared.maximumAgeOfNodesThatIAdvertiseToOthers)} if v["lastseen"] > (int(time.time()) - shared.maximumAgeOfNodesThatIAdvertiseToOthers)}
elemCount = len(filtered) elemCount = len(filtered)
if elemCount > maxAddrCount / 2: if elemCount > maxAddrCount / 2:
elemCount = int(maxAddrCount / 2) elemCount = int(maxAddrCount / 2)
addrs[stream * 2 + 1] = helper_random.randomsample(filtered.items(), elemCount) 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]: for peer, params in addrs[substream]:
templist.append((substream, peer, params["lastseen"])) templist.append((substream, peer, params["lastseen"]))
if len(templist) > 0: if templist:
self.append_write_buf(BMProto.assembleAddr(templist)) self.append_write_buf(BMProto.assembleAddr(templist))
def sendBigInv(self): def sendBigInv(self):
"""TBC"""
def sendChunk(): def sendChunk():
"""TBC"""
if objectCount == 0: if objectCount == 0:
return return
logger.debug('Sending huge inv message with %i objects to just this one peer', objectCount) 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): if Dandelion().hasHash(objHash):
continue continue
bigInvList[objHash] = 0 bigInvList[objHash] = 0
#self.objectsNewToThem[objHash] = time.time()
objectCount = 0 objectCount = 0
payload = b'' payload = b''
# Now let us start appending all of these hashes together. They will be # Now let us start appending all of these hashes together. They will be
# sent out in a big inv message to our new peer. # sent out in a big inv message to our new peer.
for hash, storedValue in bigInvList.items(): for obj_hash, _ in bigInvList.items():
payload += hash payload += obj_hash
objectCount += 1 objectCount += 1
if objectCount >= BMProto.maxObjectCount: if objectCount >= BMProto.maxObjectCount:
sendChunk() sendChunk()
@ -189,20 +203,26 @@ class TCPConnection(BMProto, TLSDispatcher):
sendChunk() sendChunk()
def handle_connect(self): def handle_connect(self):
"""TBC"""
try: try:
AdvancedDispatcher.handle_connect(self) AdvancedDispatcher.handle_connect(self)
except socket.error as e: except socket.error as e:
if e.errno in asyncore._DISCONNECTED: 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))) logger.debug("%s:%i: Connection failed: %s", self.destination.host, self.destination.port, str(e))
return return
self.nodeid = randomBytes(8) self.nodeid = randomBytes(8)
self.append_write_buf(protocol.assembleVersionMessage(self.destination.host, self.destination.port, \ self.append_write_buf(
network.connectionpool.BMConnectionPool().streams, False, nodeid=self.nodeid)) protocol.assembleVersionMessage(
#print "%s:%i: Sending version" % (self.destination.host, self.destination.port) self.destination.host,
self.destination.port,
network.connectionpool.BMConnectionPool().streams,
False,
nodeid=self.nodeid))
self.connectedAt = time.time() self.connectedAt = time.time()
receiveDataQueue.put(self.destination) receiveDataQueue.put(self.destination)
def handle_read(self): def handle_read(self):
"""TBC"""
TLSDispatcher.handle_read(self) TLSDispatcher.handle_read(self)
if self.isOutbound and self.fullyEstablished: if self.isOutbound and self.fullyEstablished:
for s in self.streams: for s in self.streams:
@ -214,9 +234,11 @@ class TCPConnection(BMProto, TLSDispatcher):
receiveDataQueue.put(self.destination) receiveDataQueue.put(self.destination)
def handle_write(self): def handle_write(self):
"""TBC"""
TLSDispatcher.handle_write(self) TLSDispatcher.handle_write(self)
def handle_close(self): def handle_close(self):
"""TBC"""
if self.isOutbound and not self.fullyEstablished: if self.isOutbound and not self.fullyEstablished:
knownnodes.decreaseRating(self.destination) knownnodes.decreaseRating(self.destination)
if self.fullyEstablished: if self.fullyEstablished:
@ -227,37 +249,55 @@ class TCPConnection(BMProto, TLSDispatcher):
class Socks5BMConnection(Socks5Connection, TCPConnection): class Socks5BMConnection(Socks5Connection, TCPConnection):
"""TBC"""
def __init__(self, address): def __init__(self, address):
Socks5Connection.__init__(self, address=address) Socks5Connection.__init__(self, address=address)
TCPConnection.__init__(self, address=address, sock=self.socket) TCPConnection.__init__(self, address=address, sock=self.socket)
self.set_state("init") self.set_state("init")
def state_proxy_handshake_done(self): def state_proxy_handshake_done(self):
"""TBC"""
Socks5Connection.state_proxy_handshake_done(self) Socks5Connection.state_proxy_handshake_done(self)
self.nodeid = randomBytes(8) self.nodeid = randomBytes(8)
self.append_write_buf(protocol.assembleVersionMessage(self.destination.host, self.destination.port, \ self.append_write_buf(
network.connectionpool.BMConnectionPool().streams, False, nodeid=self.nodeid)) 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) self.set_state("bm_header", expectBytes=protocol.Header.size)
return True return True
class Socks4aBMConnection(Socks4aConnection, TCPConnection): class Socks4aBMConnection(Socks4aConnection, TCPConnection):
"""TBC"""
def __init__(self, address): def __init__(self, address):
Socks4aConnection.__init__(self, address=address) Socks4aConnection.__init__(self, address=address)
TCPConnection.__init__(self, address=address, sock=self.socket) TCPConnection.__init__(self, address=address, sock=self.socket)
self.set_state("init") self.set_state("init")
def state_proxy_handshake_done(self): def state_proxy_handshake_done(self):
"""TBC"""
Socks4aConnection.state_proxy_handshake_done(self) Socks4aConnection.state_proxy_handshake_done(self)
self.nodeid = randomBytes(8) self.nodeid = randomBytes(8)
self.append_write_buf(protocol.assembleVersionMessage(self.destination.host, self.destination.port, \ self.append_write_buf(
network.connectionpool.BMConnectionPool().streams, False, nodeid=self.nodeid)) 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) self.set_state("bm_header", expectBytes=protocol.Header.size)
return True return True
class TCPServer(AdvancedDispatcher): 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'): if not hasattr(self, '_map'):
AdvancedDispatcher.__init__(self) AdvancedDispatcher.__init__(self)
self.create_socket(socket.AF_INET, socket.SOCK_STREAM) self.create_socket(socket.AF_INET, socket.SOCK_STREAM)
@ -280,20 +320,22 @@ class TCPServer(AdvancedDispatcher):
self.listen(5) self.listen(5)
def is_bound(self): def is_bound(self):
"""TBC"""
try: try:
return self.bound return self.bound
except AttributeError: except AttributeError:
return False return False
def handle_accept(self): def handle_accept(self):
"""TBC"""
pair = self.accept() pair = self.accept()
if pair is not None: if pair is not None:
sock, addr = pair sock, _ = pair
state.ownAddresses[state.Peer(sock.getsockname()[0], sock.getsockname()[1])] = True state.ownAddresses[state.Peer(sock.getsockname()[0], sock.getsockname()[1])] = True
if len(network.connectionpool.BMConnectionPool().inboundConnections) + \ if len(network.connectionpool.BMConnectionPool().inboundConnections) + \
len(network.connectionpool.BMConnectionPool().outboundConnections) > \ len(network.connectionpool.BMConnectionPool().outboundConnections) > \
BMConfigParser().safeGetInt("bitmessagesettings", "maxtotalconnections") + \ BMConfigParser().safeGetInt("bitmessagesettings", "maxtotalconnections") + \
BMConfigParser().safeGetInt("bitmessagesettings", "maxbootstrapconnections") + 10: BMConfigParser().safeGetInt("bitmessagesettings", "maxbootstrapconnections") + 10:
# 10 is a sort of buffer, in between it will go through the version handshake # 10 is a sort of buffer, in between it will go through the version handshake
# and return an error to the peer # and return an error to the peer
logger.warning("Server full, dropping connection") logger.warning("Server full, dropping connection")
@ -310,17 +352,7 @@ if __name__ == "__main__":
for host in (("127.0.0.1", 8448),): for host in (("127.0.0.1", 8448),):
direct = TCPConnection(host) direct = TCPConnection(host)
while len(asyncore.socket_map) > 0: while asyncore.socket_map:
print "loop, state = %s" % (direct.state) print "loop, state = %s" % (direct.state)
asyncore.loop(timeout=10, count=1) asyncore.loop(timeout=10, count=1)
continue 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)

View File

@ -1,60 +1,74 @@
#import shared # pylint: disable=too-many-branches,too-many-statements,protected-access
#import time """
#from multiprocessing import Pool, cpu_count proofofwork.py
==============
"""
from __future__ import absolute_import
import ctypes
import hashlib import hashlib
from struct import unpack, pack import os
from subprocess import call
import sys import sys
import time import time
from bmconfigparser import BMConfigParser from struct import unpack, pack
from debug import logger from subprocess import call
import paths import paths
import openclpow import openclpow
import queues import queues
import tr import tr
import os
import ctypes
import state import state
from bmconfigparser import BMConfigParser
from debug import logger
bitmsglib = 'bitmsghash.so' bitmsglib = 'bitmsghash.so'
bmpow = None bmpow = None
def _set_idle(): def _set_idle():
if 'linux' in sys.platform: if 'linux' in sys.platform:
os.nice(20) os.nice(20)
else: else:
try: try:
# pylint: disable=no-member,import-error
sys.getwindowsversion() sys.getwindowsversion()
import win32api,win32process,win32con # @UnresolvedImport import win32api
import win32process
import win32con # @UnresolvedImport
pid = win32api.GetCurrentProcessId() pid = win32api.GetCurrentProcessId()
handle = win32api.OpenProcess(win32con.PROCESS_ALL_ACCESS, True, pid) handle = win32api.OpenProcess(win32con.PROCESS_ALL_ACCESS, True, pid)
win32process.SetPriorityClass(handle, win32process.IDLE_PRIORITY_CLASS) win32process.SetPriorityClass(handle, win32process.IDLE_PRIORITY_CLASS)
except: except:
#Windows 64-bit # Windows 64-bit
pass pass
def _pool_worker(nonce, initialHash, target, pool_size): def _pool_worker(nonce, initialHash, target, pool_size):
_set_idle() _set_idle()
trialValue = float('inf') trialValue = float('inf')
while trialValue > target: while trialValue > target:
nonce += pool_size 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] return [trialValue, nonce]
def _doSafePoW(target, initialHash): def _doSafePoW(target, initialHash):
logger.debug("Safe PoW start") logger.debug("Safe PoW start")
nonce = 0 nonce = 0
trialValue = float('inf') trialValue = float('inf')
while trialValue > target and state.shutdown == 0: while trialValue > target and state.shutdown == 0:
nonce += 1 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: if state.shutdown != 0:
raise StopIteration("Interrupted") raise StopIteration("Interrupted") # pylint: misplaced-bare-raise
logger.debug("Safe PoW done") logger.debug("Safe PoW done")
return [trialValue, nonce] return [trialValue, nonce]
def _doFastPoW(target, initialHash): def _doFastPoW(target, initialHash):
logger.debug("Fast PoW start") logger.debug("Fast PoW start")
from multiprocessing import Pool, cpu_count from multiprocessing import Pool, cpu_count
@ -96,7 +110,8 @@ def _doFastPoW(target, initialHash):
logger.debug("Fast PoW done") logger.debug("Fast PoW done")
return result[0], result[1] return result[0], result[1]
time.sleep(0.2) time.sleep(0.2)
def _doCPoW(target, initialHash): def _doCPoW(target, initialHash):
h = initialHash h = initialHash
m = target m = target
@ -104,33 +119,52 @@ def _doCPoW(target, initialHash):
out_m = ctypes.c_ulonglong(m) out_m = ctypes.c_ulonglong(m)
logger.debug("C PoW start") logger.debug("C PoW start")
nonce = bmpow(out_h, out_m) 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: if state.shutdown != 0:
raise StopIteration("Interrupted") raise StopIteration("Interrupted")
logger.debug("C PoW done") logger.debug("C PoW done")
return [trialValue, nonce] return [trialValue, nonce]
def _doGPUPoW(target, initialHash): def _doGPUPoW(target, initialHash):
logger.debug("GPU PoW start") logger.debug("GPU PoW start")
nonce = openclpow.do_opencl_pow(initialHash.encode("hex"), target) nonce = openclpow.do_opencl_pow(initialHash.encode("hex"), target)
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])
#print "{} - value {} < {}".format(nonce, trialValue, target)
if trialValue > target: if trialValue > target:
deviceNames = ", ".join(gpu.name for gpu in openclpow.enabledGpus) 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))) queues.UISignalQueue.put(
logger.error("Your GPUs (%s) did not calculate correctly, disabling OpenCL. Please report to the developers.", deviceNames) (
'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 = [] openclpow.enabledGpus = []
raise Exception("GPU did not calculate correctly.") raise Exception("GPU did not calculate correctly.")
if state.shutdown != 0: if state.shutdown != 0:
raise StopIteration("Interrupted") raise StopIteration("Interrupted")
logger.debug("GPU PoW done") logger.debug("GPU PoW done")
return [trialValue, nonce] return [trialValue, nonce]
def estimate(difficulty, format = False):
def estimate(difficulty, format=False): # pylint: disable=redefined-builtin
"""
.. todo: fix unused variable
"""
ret = difficulty / 10 ret = difficulty / 10
if ret < 1: if ret < 1:
ret = 1 ret = 1
if format: if format:
# pylint: disable=unused-variable
out = str(int(ret)) + " seconds" out = str(int(ret)) + " seconds"
if ret > 60: if ret > 60:
ret /= 60 ret /= 60
@ -148,25 +182,46 @@ def estimate(difficulty, format = False):
if ret > 366: if ret > 366:
ret /= 366 ret /= 366
out = str(int(ret)) + " years" out = str(int(ret)) + " years"
else: ret = None # Ensure legacy behaviour
return ret
return ret
def getPowType(): def getPowType():
"""Get the proof of work implementation"""
if openclpow.openclEnabled(): if openclpow.openclEnabled():
return "OpenCL" return "OpenCL"
if bmpow: if bmpow:
return "C" return "C"
return "python" return "python"
def notifyBuild(tried=False): def notifyBuild(tried=False):
"""TBC"""
if bmpow: 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: 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: 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(): def buildCPoW():
"""TBC"""
if bmpow is not None: if bmpow is not None:
return return
if paths.frozen is not None: if paths.frozen is not None:
@ -190,29 +245,27 @@ def buildCPoW():
except: except:
notifyBuild(True) notifyBuild(True)
def run(target, initialHash): def run(target, initialHash):
"""Run the proof of work thread"""
if state.shutdown != 0: if state.shutdown != 0:
raise raise
target = int(target) target = int(target)
if openclpow.openclEnabled(): 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: try:
return _doGPUPoW(target, initialHash) return _doGPUPoW(target, initialHash)
except StopIteration: except StopIteration:
raise raise
except: except:
pass # fallback pass # fallback
if bmpow: if bmpow:
try: try:
return _doCPoW(target, initialHash) return _doCPoW(target, initialHash)
except StopIteration: except StopIteration:
raise raise
except: except:
pass # fallback pass # fallback
if paths.frozen == "macosx_app" or not paths.frozen: if paths.frozen == "macosx_app" or not paths.frozen:
# on my (Peter Surda) Windows 10, Windows Defender # on my (Peter Surda) Windows 10, Windows Defender
# does not like this and fights with PyBitmessage # does not like this and fights with PyBitmessage
@ -225,24 +278,30 @@ def run(target, initialHash):
raise raise
except: except:
logger.error("Fast PoW got exception:", exc_info=True) logger.error("Fast PoW got exception:", exc_info=True)
pass #fallback
try: try:
return _doSafePoW(target, initialHash) return _doSafePoW(target, initialHash)
except StopIteration: except StopIteration:
raise raise
except: except:
pass #fallback pass # fallback
def resetPoW(): def resetPoW():
"""TBC"""
openclpow.initCL() openclpow.initCL()
# init # init
def init(): def init():
global bitmsglib, bso, bmpow """TBC"""
# pylint: disable=global-statement
global bitmsglib, bmpow
openclpow.initCL() openclpow.initCL()
if "win32" == sys.platform: if sys.platform == "win32":
if ctypes.sizeof(ctypes.c_voidp) == 4: if ctypes.sizeof(ctypes.c_voidp) == 4:
bitmsglib = 'bitmsghash32.dll' bitmsglib = 'bitmsghash32.dll'
else: else:

View File

@ -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 Version 1.00
Copyright 2006 Dan-Haim. All rights reserved. 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 This module provides a standard socket-like interface for Python
for tunneling connections through SOCKS proxies. for tunneling connections through SOCKS proxies.
"""
"""
Minor modifications made by Christopher Gilbert (http://motomastyle.com/) Minor modifications made by Christopher Gilbert (http://motomastyle.com/)
for use in PyLoris (http://pyloris.sourceforge.net/) for use in PyLoris (http://pyloris.sourceforge.net/)
@ -42,7 +40,7 @@ mainly to merge bug fixes found in Sourceforge
import socket import socket
import struct import struct
import sys
PROXY_TYPE_SOCKS4 = 1 PROXY_TYPE_SOCKS4 = 1
PROXY_TYPE_SOCKS5 = 2 PROXY_TYPE_SOCKS5 = 2
@ -51,46 +49,71 @@ PROXY_TYPE_HTTP = 3
_defaultproxy = None _defaultproxy = None
_orgsocket = socket.socket _orgsocket = socket.socket
class ProxyError(Exception): pass
class GeneralProxyError(ProxyError): pass class ProxyError(Exception):
class Socks5AuthError(ProxyError): pass """Base class for other ProxyErrors"""
class Socks5Error(ProxyError): pass pass
class Socks4Error(ProxyError): pass
class HTTPError(ProxyError): 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", _generalerrors = ("success",
"invalid data", "invalid data",
"not connected", "not connected",
"not available", "not available",
"bad proxy type", "bad proxy type",
"bad input", "bad input",
"timed out", "timed out",
"network unreachable", "network unreachable",
"connection refused", "connection refused",
"host unreachable") "host unreachable")
_socks5errors = ("succeeded", _socks5errors = ("succeeded",
"general SOCKS server failure", "general SOCKS server failure",
"connection not allowed by ruleset", "connection not allowed by ruleset",
"Network unreachable", "Network unreachable",
"Host unreachable", "Host unreachable",
"Connection refused", "Connection refused",
"TTL expired", "TTL expired",
"Command not supported", "Command not supported",
"Address type not supported", "Address type not supported",
"Unknown error") "Unknown error")
_socks5autherrors = ("succeeded", _socks5autherrors = ("succeeded",
"authentication is required", "authentication is required",
"all offered authentication methods were rejected", "all offered authentication methods were rejected",
"unknown username or invalid password", "unknown username or invalid password",
"unknown error") "unknown error")
_socks4errors = ("request granted", _socks4errors = ("request granted",
"request rejected or failed", "request rejected or failed",
"request rejected because SOCKS server cannot connect to identd on the client", "request rejected because SOCKS server cannot connect to identd on the client",
"request rejected because the client program and identd report different user-ids", "request rejected because the client program and identd report different user-ids",
"unknown error") "unknown error")
def setdefaultproxy(proxytype=None, addr=None, port=None, rdns=True, username=None, password=None): def setdefaultproxy(proxytype=None, addr=None, port=None, rdns=True, username=None, password=None):
"""setdefaultproxy(proxytype, addr[, port[, rdns[, username[, password]]]]) """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 global _defaultproxy
_defaultproxy = (proxytype, addr, port, rdns, username, password) _defaultproxy = (proxytype, addr, port, rdns, username, password)
def wrapmodule(module): def wrapmodule(module):
"""wrapmodule(module) """wrapmodule(module)
Attempts to replace a module's socket library with a SOCKS socket. Must set 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; This will only work on modules that import socket directly into the namespace;
most of the Python Standard Library falls into this category. most of the Python Standard Library falls into this category.
""" """
if _defaultproxy != None: if _defaultproxy is not None:
module.socket.socket = socksocket module.socket.socket = socksocket
else: else:
raise GeneralProxyError((4, "no proxy specified")) raise GeneralProxyError((4, "no proxy specified"))
class socksocket(socket.socket): class socksocket(socket.socket):
"""socksocket([family[, type[, proto]]]) -> socket object """socksocket([family[, type[, proto]]]) -> socket object
Open a SOCKS enabled socket. The parameters are the same as 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): 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) _orgsocket.__init__(self, family, type, proto, _sock)
if _defaultproxy != None: if _defaultproxy is not None:
self.__proxy = _defaultproxy self.__proxy = _defaultproxy
else: else:
self.__proxy = (None, None, None, None, None, None) self.__proxy = (None, None, None, None, None, None)
@ -138,8 +164,9 @@ class socksocket(socket.socket):
except socket.timeout: except socket.timeout:
raise GeneralProxyError((6, "timed out")) raise GeneralProxyError((6, "timed out"))
while len(data) < count: while len(data) < count:
d = self.recv(count-len(data)) d = self.recv(count - len(data))
if not d: raise GeneralProxyError((0, "connection closed unexpectedly")) if not d:
raise GeneralProxyError((0, "connection closed unexpectedly"))
data = data + d data = data + d
return data return data
@ -167,7 +194,7 @@ class socksocket(socket.socket):
Negotiates a connection through a SOCKS5 server. Negotiates a connection through a SOCKS5 server.
""" """
# First we'll send the authentication packages we support. # 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 # The username/password details were supplied to the
# setproxy method so we support the USERNAME/PASSWORD # setproxy method so we support the USERNAME/PASSWORD
# authentication (in addition to the standard none). # authentication (in addition to the standard none).
@ -189,7 +216,11 @@ class socksocket(socket.socket):
elif chosenauth[1:2] == chr(0x02).encode(): elif chosenauth[1:2] == chr(0x02).encode():
# Okay, we need to perform a basic username/password # Okay, we need to perform a basic username/password
# authentication. # 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) authstat = self.__recvall(2)
if authstat[0:1] != chr(0x01).encode(): if authstat[0:1] != chr(0x01).encode():
# Bad response # Bad response
@ -207,7 +238,7 @@ class socksocket(socket.socket):
raise Socks5AuthError((2, _socks5autherrors[2])) raise Socks5AuthError((2, _socks5autherrors[2]))
else: else:
raise GeneralProxyError((1, _generalerrors[1])) raise GeneralProxyError((1, _generalerrors[1]))
def __connectsocks5(self, destaddr, destport): def __connectsocks5(self, destaddr, destport):
# Now we can request the actual connection # Now we can request the actual connection
req = struct.pack('BBB', 0x05, 0x01, 0x00) req = struct.pack('BBB', 0x05, 0x01, 0x00)
@ -236,7 +267,7 @@ class socksocket(socket.socket):
elif resp[1:2] != chr(0x00).encode(): elif resp[1:2] != chr(0x00).encode():
# Connection failed # Connection failed
self.close() 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])])) raise Socks5Error((ord(resp[1:2]), _socks5errors[ord(resp[1:2])]))
else: else:
raise Socks5Error((9, _socks5errors[9])) raise Socks5Error((9, _socks5errors[9]))
@ -248,10 +279,10 @@ class socksocket(socket.socket):
boundaddr = self.__recvall(ord(resp[4:5])) boundaddr = self.__recvall(ord(resp[4:5]))
else: else:
self.close() self.close()
raise GeneralProxyError((1,_generalerrors[1])) raise GeneralProxyError((1, _generalerrors[1]))
boundport = struct.unpack(">H", self.__recvall(2))[0] boundport = struct.unpack(">H", self.__recvall(2))[0]
self.__proxysockname = (boundaddr, boundport) self.__proxysockname = (boundaddr, boundport)
if ipaddr != None: if ipaddr is not None:
self.__proxypeername = (socket.inet_ntoa(ipaddr), destport) self.__proxypeername = (socket.inet_ntoa(ipaddr), destport)
else: else:
self.__proxypeername = (destaddr, destport) self.__proxypeername = (destaddr, destport)
@ -271,7 +302,7 @@ class socksocket(socket.socket):
elif resp[1:2] != chr(0x00).encode(): elif resp[1:2] != chr(0x00).encode():
# Connection failed # Connection failed
self.close() 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])])) raise Socks5Error((ord(resp[1:2]), _socks5errors[ord(resp[1:2])]))
else: else:
raise Socks5Error((9, _socks5errors[9])) raise Socks5Error((9, _socks5errors[9]))
@ -283,10 +314,10 @@ class socksocket(socket.socket):
ip = self.__recvall(ord(resp[4:5])) ip = self.__recvall(ord(resp[4:5]))
else: else:
self.close() self.close()
raise GeneralProxyError((1,_generalerrors[1])) raise GeneralProxyError((1, _generalerrors[1]))
boundport = struct.unpack(">H", self.__recvall(2))[0] _ = struct.unpack(">H", self.__recvall(2))[0]
return ip return ip
def getproxysockname(self): def getproxysockname(self):
"""getsockname() -> address info """getsockname() -> address info
Returns the bound IP address and port number at the proxy. Returns the bound IP address and port number at the proxy.
@ -307,9 +338,10 @@ class socksocket(socket.socket):
return self.__proxypeername return self.__proxypeername
def getproxytype(self): def getproxytype(self):
"""Get the proxy type"""
return self.__proxy[0] return self.__proxy[0]
def __negotiatesocks4(self,destaddr,destport): def __negotiatesocks4(self, destaddr, destport):
"""__negotiatesocks4(self,destaddr,destport) """__negotiatesocks4(self,destaddr,destport)
Negotiates a connection through a SOCKS4 server. Negotiates a connection through a SOCKS4 server.
""" """
@ -327,7 +359,7 @@ class socksocket(socket.socket):
# Construct the request packet # Construct the request packet
req = struct.pack(">BBH", 0x04, 0x01, destport) + ipaddr req = struct.pack(">BBH", 0x04, 0x01, destport) + ipaddr
# The username parameter is considered userid for SOCKS4 # 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 + self.__proxy[4]
req = req + chr(0x00).encode() req = req + chr(0x00).encode()
# DNS name if remote resolving is required # DNS name if remote resolving is required
@ -341,7 +373,7 @@ class socksocket(socket.socket):
if resp[0:1] != chr(0x00).encode(): if resp[0:1] != chr(0x00).encode():
# Bad data # Bad data
self.close() self.close()
raise GeneralProxyError((1,_generalerrors[1])) raise GeneralProxyError((1, _generalerrors[1]))
if resp[1:2] != chr(0x5A).encode(): if resp[1:2] != chr(0x5A).encode():
# Server returned an error # Server returned an error
self.close() self.close()
@ -352,7 +384,7 @@ class socksocket(socket.socket):
raise Socks4Error((94, _socks4errors[4])) raise Socks4Error((94, _socks4errors[4]))
# Get the bound address/port # Get the bound address/port
self.__proxysockname = (socket.inet_ntoa(resp[4:]), struct.unpack(">H", resp[2:4])[0]) 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) self.__proxypeername = (socket.inet_ntoa(ipaddr), destport)
else: else:
self.__proxypeername = (destaddr, destport) self.__proxypeername = (destaddr, destport)
@ -366,7 +398,16 @@ class socksocket(socket.socket):
addr = socket.gethostbyname(destaddr) addr = socket.gethostbyname(destaddr)
else: else:
addr = destaddr 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" # We read the response until we get the string "\r\n\r\n"
resp = self.recv(1) resp = self.recv(1)
while resp.find("\r\n\r\n".encode()) == -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(). To select the proxy server use setproxy().
""" """
# Do a minimal input check first # 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])) raise GeneralProxyError((5, _generalerrors[5]))
if self.__proxy[0] == PROXY_TYPE_SOCKS5: if self.__proxy[0] == PROXY_TYPE_SOCKS5:
if self.__proxy[2] != None: if self.__proxy[2] is not None:
portnum = self.__proxy[2] portnum = self.__proxy[2]
else: else:
portnum = 1080 portnum = 1080
@ -419,19 +465,19 @@ class socksocket(socket.socket):
self.__negotiatesocks5() self.__negotiatesocks5()
self.__connectsocks5(destpair[0], destpair[1]) self.__connectsocks5(destpair[0], destpair[1])
elif self.__proxy[0] == PROXY_TYPE_SOCKS4: elif self.__proxy[0] == PROXY_TYPE_SOCKS4:
if self.__proxy[2] != None: if self.__proxy[2] is not None:
portnum = self.__proxy[2] portnum = self.__proxy[2]
else: else:
portnum = 1080 portnum = 1080
_orgsocket.connect(self,(self.__proxy[1], portnum)) _orgsocket.connect(self, (self.__proxy[1], portnum))
self.__negotiatesocks4(destpair[0], destpair[1]) self.__negotiatesocks4(destpair[0], destpair[1])
elif self.__proxy[0] == PROXY_TYPE_HTTP: elif self.__proxy[0] == PROXY_TYPE_HTTP:
if self.__proxy[2] != None: if self.__proxy[2] is not None:
portnum = self.__proxy[2] portnum = self.__proxy[2]
else: else:
portnum = 8080 portnum = 8080
try: try:
_orgsocket.connect(self,(self.__proxy[1], portnum)) _orgsocket.connect(self, (self.__proxy[1], portnum))
except socket.error as e: except socket.error as e:
# ENETUNREACH, WSAENETUNREACH # ENETUNREACH, WSAENETUNREACH
if e[0] in [101, 10051]: if e[0] in [101, 10051]:
@ -444,14 +490,15 @@ class socksocket(socket.socket):
raise GeneralProxyError((9, _generalerrors[9])) raise GeneralProxyError((9, _generalerrors[9]))
raise raise
self.__negotiatehttp(destpair[0], destpair[1]) self.__negotiatehttp(destpair[0], destpair[1])
elif self.__proxy[0] == None: elif self.__proxy[0] is None:
_orgsocket.connect(self, (destpair[0], destpair[1])) _orgsocket.connect(self, (destpair[0], destpair[1]))
else: else:
raise GeneralProxyError((4, _generalerrors[4])) raise GeneralProxyError((4, _generalerrors[4]))
def resolve(self, host): def resolve(self, host):
"""TBC"""
if self.__proxy[0] == PROXY_TYPE_SOCKS5: if self.__proxy[0] == PROXY_TYPE_SOCKS5:
if self.__proxy[2] != None: if self.__proxy[2] is not None:
portnum = self.__proxy[2] portnum = self.__proxy[2]
else: else:
portnum = 1080 portnum = 1080

View File

@ -1,21 +1,33 @@
# A simple upnp module to forward port for BitMessage # pylint: disable=too-many-statements,too-many-branches,protected-access,no-self-use
# Reference: http://mattscodecave.com/posts/using-python-and-upnp-to-forward-a-port """
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 import httplib
from random import randint from random import randint
import socket import socket
from struct import unpack, pack from struct import unpack
import threading import threading
import time import time
import urllib2
from urlparse import urlparse
from xml.dom.minidom import Document, parseString
from bmconfigparser import BMConfigParser from bmconfigparser import BMConfigParser
from debug import logger
from network.connectionpool import BMConnectionPool from network.connectionpool import BMConnectionPool
from helper_threading import * from helper_threading import StoppableThread
import queues import queues
import shared import shared
import state import state
import tr import tr
def createRequestXML(service, action, arguments=None): def createRequestXML(service, action, arguments=None):
from xml.dom.minidom import Document """Router UPnP requests are XML formatted"""
doc = Document() doc = Document()
@ -63,22 +75,24 @@ def createRequestXML(service, action, arguments=None):
# our tree is ready, conver it to a string # our tree is ready, conver it to a string
return doc.toxml() 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 = "" name = ""
path = "" path = ""
address = None address = None
routerPath = None routerPath = None
extPort = None extPort = None
def __init__(self, ssdpResponse, address): def __init__(self, ssdpResponse, address):
import urllib2
from xml.dom.minidom import parseString
from urlparse import urlparse
from debug import logger
self.address = address self.address = address
@ -92,9 +106,9 @@ class Router:
try: try:
self.routerPath = urlparse(header['location']) self.routerPath = urlparse(header['location'])
if not self.routerPath or not hasattr(self.routerPath, "hostname"): 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: 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 # get the profile xml file and read it into a variable
directory = urllib2.urlopen(header['location']).read() directory = urllib2.urlopen(header['location']).read()
@ -108,45 +122,58 @@ class Router:
for service in service_types: for service in service_types:
if service.childNodes[0].data.find('WANIPConnection') > 0 or \ 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.path = service.parentNode.getElementsByTagName('controlURL')[0].childNodes[0].data
self.upnp_schema = service.childNodes[0].data.split(':')[-2] self.upnp_schema = service.childNodes[0].data.split(':')[-2]
def AddPortMapping(self, externalPort, internalPort, internalClient, protocol, description, leaseDuration = 0, enabled = 1): def AddPortMapping(
from debug import logger 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', [ resp = self.soapRequest(self.upnp_schema + ':1', 'AddPortMapping', [
('NewRemoteHost', ''), ('NewRemoteHost', ''),
('NewExternalPort', str(externalPort)), ('NewExternalPort', str(externalPort)),
('NewProtocol', protocol), ('NewProtocol', protocol),
('NewInternalPort', str(internalPort)), ('NewInternalPort', str(internalPort)),
('NewInternalClient', internalClient), ('NewInternalClient', internalClient),
('NewEnabled', str(enabled)), ('NewEnabled', str(enabled)),
('NewPortMappingDescription', str(description)), ('NewPortMappingDescription', str(description)),
('NewLeaseDuration', str(leaseDuration)) ('NewLeaseDuration', str(leaseDuration))
]) ])
self.extPort = externalPort 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 return resp
def DeletePortMapping(self, externalPort, protocol): def DeletePortMapping(self, externalPort, protocol):
from debug import logger """Delete UPnP port mapping"""
resp = self.soapRequest(self.upnp_schema + ':1', 'DeletePortMapping', [ resp = self.soapRequest(self.upnp_schema + ':1', 'DeletePortMapping', [
('NewRemoteHost', ''), ('NewRemoteHost', ''),
('NewExternalPort', str(externalPort)), ('NewExternalPort', str(externalPort)),
('NewProtocol', protocol), ('NewProtocol', protocol),
]) ])
logger.info("Removed UPnP mapping on external port %i", externalPort) logger.info("Removed UPnP mapping on external port %i", externalPort)
return resp return resp
def GetExternalIPAddress(self): def GetExternalIPAddress(self):
from xml.dom.minidom import parseString """Get the external address"""
resp = self.soapRequest(self.upnp_schema + ':1', 'GetExternalIPAddress') resp = self.soapRequest(self.upnp_schema + ':1', 'GetExternalIPAddress')
dom = parseString(resp) dom = parseString(resp)
return dom.getElementsByTagName('NewExternalIPAddress')[0].childNodes[0].data return dom.getElementsByTagName('NewExternalIPAddress')[0].childNodes[0].data
def soapRequest(self, service, action, arguments=None): def soapRequest(self, service, action, arguments=None):
from xml.dom.minidom import parseString """Make a request to a router"""
from debug import logger
conn = httplib.HTTPConnection(self.routerPath.hostname, self.routerPath.port) conn = httplib.HTTPConnection(self.routerPath.hostname, self.routerPath.port)
conn.request( conn.request(
'POST', 'POST',
@ -155,8 +182,8 @@ class Router:
{ {
'SOAPAction': '"urn:schemas-upnp-org:service:%s#%s"' % (service, action), 'SOAPAction': '"urn:schemas-upnp-org:service:%s#%s"' % (service, action),
'Content-Type': 'text/xml' 'Content-Type': 'text/xml'
} }
) )
resp = conn.getresponse() resp = conn.getresponse()
conn.close() conn.close()
if resp.status == 500: if resp.status == 500:
@ -164,21 +191,24 @@ class Router:
try: try:
dom = parseString(respData) dom = parseString(respData)
errinfo = dom.getElementsByTagName('errorDescription') errinfo = dom.getElementsByTagName('errorDescription')
if len(errinfo) > 0: if errinfo:
logger.error("UPnP error: %s", respData) logger.error("UPnP error: %s", respData)
raise UPnPError(errinfo[0].childNodes[0].data) raise UPnPError(errinfo[0].childNodes[0].data)
except: except:
raise UPnPError("Unable to parse SOAP error: %s" %(respData)) raise UPnPError("Unable to parse SOAP error: %s" % (respData))
return resp return resp
class uPnPThread(threading.Thread, StoppableThread): class uPnPThread(threading.Thread, StoppableThread):
"""Start a thread to handle UPnP activity"""
SSDP_ADDR = "239.255.255.250" SSDP_ADDR = "239.255.255.250"
GOOGLE_DNS = "8.8.8.8" GOOGLE_DNS = "8.8.8.8"
SSDP_PORT = 1900 SSDP_PORT = 1900
SSDP_MX = 2 SSDP_MX = 2
SSDP_ST = "urn:schemas-upnp-org:device:InternetGatewayDevice:1" SSDP_ST = "urn:schemas-upnp-org:device:InternetGatewayDevice:1"
def __init__ (self): def __init__(self):
threading.Thread.__init__(self, name="uPnPThread") threading.Thread.__init__(self, name="uPnPThread")
try: try:
self.extPort = BMConfigParser().getint('bitmessagesettings', 'extport') self.extPort = BMConfigParser().getint('bitmessagesettings', 'extport')
@ -194,8 +224,8 @@ class uPnPThread(threading.Thread, StoppableThread):
self.initStop() self.initStop()
def run(self): def run(self):
from debug import logger """Start the thread to manage UPnP activity"""
logger.debug("Starting UPnP thread") logger.debug("Starting UPnP thread")
logger.debug("Local IP: %s", self.localIP) logger.debug("Local IP: %s", self.localIP)
lastSent = 0 lastSent = 0
@ -209,9 +239,11 @@ class uPnPThread(threading.Thread, StoppableThread):
if not bound: if not bound:
time.sleep(1) time.sleep(1)
# pylint: disable=attribute-defined-outside-init
self.localPort = BMConfigParser().getint('bitmessagesettings', 'port') self.localPort = BMConfigParser().getint('bitmessagesettings', 'port')
while state.shutdown == 0 and BMConfigParser().safeGetBoolean('bitmessagesettings', 'upnp'): 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: try:
self.sendSearchRouter() self.sendSearchRouter()
except: except:
@ -219,7 +251,7 @@ class uPnPThread(threading.Thread, StoppableThread):
lastSent = time.time() lastSent = time.time()
try: try:
while state.shutdown == 0 and BMConfigParser().safeGetBoolean('bitmessagesettings', 'upnp'): 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: if resp is None:
continue continue
newRouter = Router(resp, ip) newRouter = Router(resp, ip)
@ -230,14 +262,16 @@ class uPnPThread(threading.Thread, StoppableThread):
logger.debug("Found UPnP router at %s", ip) logger.debug("Found UPnP router at %s", ip)
self.routers.append(newRouter) self.routers.append(newRouter)
self.createPortMapping(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 # retry connections so that the submitted port is refreshed
with shared.alreadyAttemptedConnectionsListLock: with shared.alreadyAttemptedConnectionsListLock:
shared.alreadyAttemptedConnectionsList.clear() shared.alreadyAttemptedConnectionsList.clear()
shared.alreadyAttemptedConnectionsListResetTime = int( shared.alreadyAttemptedConnectionsListResetTime = int(
time.time()) time.time())
break break
except socket.timeout as e: except socket.timeout:
pass pass
except: except:
logger.error("Failure running UPnP router search.", exc_info=True) logger.error("Failure running UPnP router search.", exc_info=True)
@ -259,22 +293,25 @@ class uPnPThread(threading.Thread, StoppableThread):
self.deletePortMapping(router) self.deletePortMapping(router)
shared.extPort = None shared.extPort = None
if deleted: 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") logger.debug("UPnP thread done")
def getLocalIP(self): def getLocalIP(self):
"""Get the local IP of the node"""
s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
s.setsockopt(socket.SOL_SOCKET, socket.SO_BROADCAST, 1) s.setsockopt(socket.SOL_SOCKET, socket.SO_BROADCAST, 1)
s.connect((uPnPThread.GOOGLE_DNS, 1)) s.connect((uPnPThread.GOOGLE_DNS, 1))
return s.getsockname()[0] return s.getsockname()[0]
def sendSearchRouter(self): def sendSearchRouter(self):
from debug import logger """Querying for UPnP services"""
ssdpRequest = "M-SEARCH * HTTP/1.1\r\n" + \ ssdpRequest = "M-SEARCH * HTTP/1.1\r\n" + \
"HOST: %s:%d\r\n" % (uPnPThread.SSDP_ADDR, uPnPThread.SSDP_PORT) + \ "HOST: %s:%d\r\n" % (uPnPThread.SSDP_ADDR, uPnPThread.SSDP_PORT) + \
"MAN: \"ssdp:discover\"\r\n" + \ "MAN: \"ssdp:discover\"\r\n" + \
"MX: %d\r\n" % (uPnPThread.SSDP_MX, ) + \ "MX: %d\r\n" % (uPnPThread.SSDP_MX, ) + \
"ST: %s\r\n" % (uPnPThread.SSDP_ST, ) + "\r\n" "ST: %s\r\n" % (uPnPThread.SSDP_ST, ) + "\r\n"
try: try:
logger.debug("Sending UPnP query") logger.debug("Sending UPnP query")
@ -283,19 +320,24 @@ class uPnPThread(threading.Thread, StoppableThread):
logger.exception("UPnP send query failed") logger.exception("UPnP send query failed")
def createPortMapping(self, router): def createPortMapping(self, router):
from debug import logger """Add a port mapping"""
for i in range(50): for i in range(50):
try: try:
routerIP, = unpack('>I', socket.inet_aton(router.address)) _, = unpack('>I', socket.inet_aton(router.address))
localIP = self.localIP localIP = self.localIP
if i == 0: if i == 0:
extPort = self.localPort # try same port first extPort = self.localPort # try same port first
elif i == 1 and self.extPort: 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: else:
extPort = randint(32767, 65535) 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') router.AddPortMapping(extPort, self.localPort, localIP, 'TCP', 'BitMessage')
shared.extPort = extPort shared.extPort = extPort
self.extPort = extPort self.extPort = extPort
@ -306,7 +348,5 @@ class uPnPThread(threading.Thread, StoppableThread):
logger.debug("UPnP error: ", exc_info=True) logger.debug("UPnP error: ", exc_info=True)
def deletePortMapping(self, router): def deletePortMapping(self, router):
"""Delete a port mapping"""
router.DeletePortMapping(router.extPort, 'TCP') router.DeletePortMapping(router.extPort, 'TCP')