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

Closed
coffeedogs wants to merge 1 commits from codeq31-05 into v0.6
11 changed files with 1272 additions and 656 deletions

View File

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

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-2018 The Bitmessage developers
"""
This is not what you run to run the Bitmessage API. Instead, enable the API
( https://bitmessage.org/wiki/API ) and optionally enable daemon mode
( https://bitmessage.org/wiki/Daemon ) then run bitmessagemain.py.
"""
from __future__ import absolute_import
import base64
import hashlib
import json
from struct import pack
import time
g1itch commented 2018-06-08 14:59:13 +02:00 (Migrated from github.com)
Review

Why?

Why?
coffeedogs commented 2018-06-13 20:05:51 +02:00 (Migrated from github.com)
Review

In the wrong order: Continued in PR#1271

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

View File

@ -1,37 +1,40 @@
#!/usr/bin/python2.7
# Copyright (c) 2012-2016 Jonathan Warren
# Copyright (c) 2012-2018 The Bitmessage developers
# Distributed under the MIT/X11 software license. See the accompanying
# file COPYING or http://www.opensource.org/licenses/mit-license.php.
# pylint: disable=no-self-use,too-many-branches,too-many-statements,too-many-locals
"""
bitmessagemain.py
=================
# Right now, PyBitmessage only support connecting to stream 1. It doesn't
# yet contain logic to expand into further streams.
Copyright (c) 2012-2016 Jonathan Warren
Copyright (c) 2012-2018 The Bitmessage developers
Distributed under the MIT/X11 software license. See the accompanying
file COPYING or http://www.opensource.org/licenses/mit-license.php.
# The software version variable is now held in shared.py
Right now, PyBitmessage only support connecting to stream 1. It doesn't
yet contain logic to expand into further streams.
import os
import sys
The software version variable is now held in shared.py
g1itch commented 2018-06-08 15:08:14 +02:00 (Migrated from github.com)
Review

Why are you moving copyrights to docstring?

Why are you moving copyrights to docstring?
coffeedogs commented 2018-06-13 20:21:53 +02:00 (Migrated from github.com)
Review

Multi-line comments are better done with a triple quoted string. In particular, Sphinx (or rather effective rST comments) requires triple quoted strings. However a triple-quoted string is flagged by linters as 'string statement has no effect'. Perhaps a Sphinx section on copyright would be the best place to put these notices? Being part of a module docstring made more sense to me than loose in the top level in module, though I don't feel a Python file is the right place anyway. So continued in PR#1270.

Multi-line comments are better done with a triple quoted string. In particular, Sphinx (or rather effective rST comments) requires triple quoted strings. However a triple-quoted string is flagged by linters as 'string statement has no effect'. Perhaps a Sphinx section on copyright would be the best place to put these notices? Being part of a module docstring made more sense to me than loose in the top level in module, though I don't feel a Python file is the right place anyway. So continued in PR#1270.
app_dir = os.path.dirname(os.path.abspath(__file__))
os.chdir(app_dir)
sys.path.insert(0, app_dir)
"""
from __future__ import absolute_import
import depends
depends.check_dependencies()
# Used to capture a Ctrl-C keypress so that Bitmessage can shutdown gracefully.
import signal
# The next 3 are used for the API
from singleinstance import singleinstance
import errno
import socket
import ctypes
import errno
import getopt
import os
import signal
import socket
import sys
g1itch commented 2018-06-08 15:06:33 +02:00 (Migrated from github.com)
Review

This will probably break script execution.

This will probably break script execution.
import threading
from random import randint
from struct import pack
from subprocess import call
from time import sleep
from random import randint
import getopt
# Used to capture a Ctrl-C keypress so that Bitmessage can shutdown gracefully.
# The next 3 are used for the API
from singleinstance import singleinstance
from api import MySimpleXMLRPCRequestHandler, StoppableXMLRPCServer
from helper_startup import (
@ -39,11 +42,11 @@ from helper_startup import (
)
import defaults
import depends
import shared
import knownnodes
import state
import shutdown
import threading
# Classes
from class_sqlThread import sqlThread
@ -72,7 +75,12 @@ import helper_generic
import helper_threading
depends.check_dependencies()
def connectToStream(streamNumber):
"""Connect to a stream"""
state.streamsInWhichIAmParticipating.append(streamNumber)
selfInitiatedConnections[streamNumber] = {}
@ -93,10 +101,10 @@ def connectToStream(streamNumber):
with knownnodes.knownNodesLock:
if streamNumber not in knownnodes.knownNodes:
knownnodes.knownNodes[streamNumber] = {}
if streamNumber*2 not in knownnodes.knownNodes:
knownnodes.knownNodes[streamNumber*2] = {}
if streamNumber*2+1 not in knownnodes.knownNodes:
knownnodes.knownNodes[streamNumber*2+1] = {}
if streamNumber * 2 not in knownnodes.knownNodes:
knownnodes.knownNodes[streamNumber * 2] = {}
if streamNumber * 2 + 1 not in knownnodes.knownNodes:
knownnodes.knownNodes[streamNumber * 2 + 1] = {}
BMConnectionPool().connectToStream(streamNumber)
@ -114,6 +122,8 @@ def _fixSocket():
addressToString = ctypes.windll.ws2_32.WSAAddressToStringA
def inet_ntop(family, host):
"""Convert IPv4 and IPv6 addresses from binary to text form"""
if family == socket.AF_INET:
if len(host) != 4:
raise ValueError("invalid IPv4 host")
@ -135,6 +145,8 @@ def _fixSocket():
stringToAddress = ctypes.windll.ws2_32.WSAStringToAddressA
def inet_pton(family, host):
"""Convert IPv4 and IPv6 addresses from text to binary form"""
buf = "\0" * 28
lengthBuf = pack("I", len(buf))
if stringToAddress(str(host),
@ -158,13 +170,15 @@ def _fixSocket():
socket.IPV6_V6ONLY = 27
# This thread, of which there is only one, runs the API.
class singleAPI(threading.Thread, helper_threading.StoppableThread):
"""This thread, of which there is only one, runs the API."""
def __init__(self):
threading.Thread.__init__(self, name="singleAPI")
self.initStop()
def stopThread(self):
"""Stop the API thread"""
super(singleAPI, self).stopThread()
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
try:
@ -178,9 +192,10 @@ class singleAPI(threading.Thread, helper_threading.StoppableThread):
pass
def run(self):
"""Run the API thread"""
port = BMConfigParser().getint('bitmessagesettings', 'apiport')
try:
from errno import WSAEADDRINUSE
from errno import WSAEADDRINUSE # pylint: disable=unused-variable
except (ImportError, AttributeError):
errno.WSAEADDRINUSE = errno.EADDRINUSE
for attempt in range(50):
@ -215,15 +230,18 @@ if shared.useVeryEasyProofOfWorkForTesting:
defaults.networkDefaultPayloadLengthExtraBytes / 100)
class Main:
class Main(object):
"""The main app"""
def start(self):
"""Start the main app"""
_fixSocket()
daemon = BMConfigParser().safeGetBoolean(
'bitmessagesettings', 'daemon')
try:
opts, args = getopt.getopt(
opts, _ = getopt.getopt(
sys.argv[1:], "hcdt",
["help", "curses", "daemon", "test"])
@ -231,7 +249,7 @@ class Main:
self.usage()
sys.exit(2)
for opt, arg in opts:
for opt, _ in opts:
if opt in ("-h", "--help"):
self.usage()
sys.exit()
@ -249,7 +267,7 @@ class Main:
if daemon and not state.testmode:
with shared.printLock:
print('Running as a daemon. Send TERM signal to end.')
print 'Running as a daemon. Send TERM signal to end.'
self.daemonize()
self.setSignalHandler()
@ -381,8 +399,7 @@ class Main:
BMConfigParser().remove_option('bitmessagesettings', 'dontconnect')
elif daemon is False:
if state.curses:
# if depends.check_curses():
print('Running with curses')
print 'Running with curses'
import bitmessagecurses
bitmessagecurses.runwrapper()
elif depends.check_pyqt():
@ -411,6 +428,7 @@ class Main:
sleep(1)
def daemonize(self):
"""Daemonise"""
grandfatherPid = os.getpid()
parentPid = None
try:
@ -420,7 +438,7 @@ class Main:
# wait until grandchild ready
while True:
sleep(1)
os._exit(0)
sys.exit(0)
except AttributeError:
# fork not implemented
pass
@ -441,7 +459,7 @@ class Main:
# wait until child ready
while True:
sleep(1)
os._exit(0)
sys.exit(0)
except AttributeError:
# fork not implemented
pass
@ -463,11 +481,13 @@ class Main:
os.kill(grandfatherPid, signal.SIGTERM)
def setSignalHandler(self):
"""Register signal handlers"""
signal.signal(signal.SIGINT, helper_generic.signal_handler)
signal.signal(signal.SIGTERM, helper_generic.signal_handler)
# signal.signal(signal.SIGINT, signal.SIG_DFL)
def usage(self):
"""Print usage message"""
print 'Usage: ' + sys.argv[0] + ' [OPTIONS]'
print '''
Options:
@ -480,12 +500,18 @@ All parameters are optional.
'''
def stop(self):
"""Stop the daemon"""
with shared.printLock:
print('Stopping Bitmessage Deamon.')
print 'Stopping Bitmessage Daemon.'
shutdown.doCleanShutdown()
# TODO: nice function but no one is using this
def getApiAddress(self):
"""
Return the address and port the API is configured to use
.. todo:: nice function but no one is using this
"""
if not BMConfigParser().safeGetBoolean(
'bitmessagesettings', 'apienabled'):
return None
@ -495,14 +521,10 @@ All parameters are optional.
def main():
"""Create and start the main app"""
mainprogram = Main()
mainprogram.start()
if __name__ == "__main__":
main()
# So far, the creation of and management of the Bitmessage protocol and this
# client is a one-man operation. Bitcoin tips are quite appreciated.
# 1H5XaDA6fYENLbknwZyjiYXYPQaFjjLX2u

View File

@ -1,20 +1,29 @@
# -*- coding: utf-8 -*-
# pylint: disable=too-many-locals
"""
Form implementation generated from reading ui file 'bitmessageui.ui'
# Form implementation generated from reading ui file 'bitmessageui.ui'
#
# Created: Mon Mar 23 22:18:07 2015
# by: PyQt4 UI code generator 4.10.4
#
# WARNING! All changes made in this file will be lost!
Created: Mon Mar 23 22:18:07 2015
by: PyQt4 UI code generator 4.10.4
WARNING! All changes made in this file will be lost!
"""
from __future__ import absolute_import
import sys
from PyQt4 import QtCore, QtGui
from bmconfigparser import BMConfigParser
from foldertree import AddressBookCompleter
from messageview import MessageView
from messagecompose import MessageCompose
import settingsmixin
from networkstatus import NetworkStatus
from blacklist import Blacklist
from . import bitmessage_icons_rc # pylint: disable=unused-import
from . import settingsmixin
from .messageview import MessageView
from .messagecompose import MessageCompose
from .networkstatus import NetworkStatus
from .blacklist import Blacklist
from .foldertree import AddressBookCompleter
try:
_fromUtf8 = QtCore.QString.fromUtf8
@ -24,24 +33,38 @@ except AttributeError:
try:
_encoding = QtGui.QApplication.UnicodeUTF8
def _translate(context, text, disambig, encoding = QtCore.QCoreApplication.CodecForTr, n = None):
def _translate(context, text, disambig, encoding=QtCore.QCoreApplication.CodecForTr, n=None):
g1itch commented 2018-06-08 14:48:03 +02:00 (Migrated from github.com)
Review

This changes break portable usage.

This changes break portable usage.
coffeedogs commented 2018-06-13 20:02:19 +02:00 (Migrated from github.com)
Review

Continued in PR#1271

Continued in PR#1271
# pylint: disable=unused-argument
if n is None:
return QtGui.QApplication.translate(context, text, disambig, _encoding)
else:
return QtGui.QApplication.translate(context, text, disambig, _encoding, n)
return QtGui.QApplication.translate(context, text, disambig, _encoding, n)
except AttributeError:
def _translate(context, text, disambig, encoding = QtCore.QCoreApplication.CodecForTr, n = None):
def _translate(context, text, disambig, encoding=QtCore.QCoreApplication.CodecForTr, n=None):
# pylint: disable=unused-argument
if n is None:
return QtGui.QApplication.translate(context, text, disambig)
else:
return QtGui.QApplication.translate(context, text, disambig, QtCore.QCoreApplication.CodecForTr, n)
return QtGui.QApplication.translate(context, text, disambig, QtCore.QCoreApplication.CodecForTr, n)
class Ui_MainWindow(object):
"""Encapsulate the main UI"""
# pylint: disable=too-many-instance-attributes,too-many-statements
def setupUi(self, MainWindow):
"""Set up the UI"""
# pylint: disable=attribute-defined-outside-init
MainWindow.setObjectName(_fromUtf8("MainWindow"))
MainWindow.resize(885, 580)
icon = QtGui.QIcon()
icon.addPixmap(QtGui.QPixmap(_fromUtf8(":/newPrefix/images/can-icon-24px.png")), QtGui.QIcon.Normal, QtGui.QIcon.Off)
icon.addPixmap(
QtGui.QPixmap(
_fromUtf8(":/newPrefix/images/can-icon-24px.png")),
QtGui.QIcon.Normal,
QtGui.QIcon.Off)
MainWindow.setWindowIcon(icon)
MainWindow.setTabShape(QtGui.QTabWidget.Rounded)
self.centralwidget = QtGui.QWidget(MainWindow)
@ -75,7 +98,11 @@ class Ui_MainWindow(object):
self.treeWidgetYourIdentities.setObjectName(_fromUtf8("treeWidgetYourIdentities"))
self.treeWidgetYourIdentities.resize(200, self.treeWidgetYourIdentities.height())
icon1 = QtGui.QIcon()
icon1.addPixmap(QtGui.QPixmap(_fromUtf8(":/newPrefix/images/identities.png")), QtGui.QIcon.Selected, QtGui.QIcon.Off)
icon1.addPixmap(
QtGui.QPixmap(
_fromUtf8(":/newPrefix/images/identities.png")),
QtGui.QIcon.Selected,
QtGui.QIcon.Off)
self.treeWidgetYourIdentities.headerItem().setIcon(0, icon1)
self.verticalSplitter_12.addWidget(self.treeWidgetYourIdentities)
self.pushButtonNewAddress = QtGui.QPushButton(self.inbox)
@ -175,7 +202,11 @@ class Ui_MainWindow(object):
self.tableWidgetAddressBook.resize(200, self.tableWidgetAddressBook.height())
item = QtGui.QTableWidgetItem()
icon3 = QtGui.QIcon()
icon3.addPixmap(QtGui.QPixmap(_fromUtf8(":/newPrefix/images/addressbook.png")), QtGui.QIcon.Selected, QtGui.QIcon.Off)
icon3.addPixmap(
QtGui.QPixmap(
_fromUtf8(":/newPrefix/images/addressbook.png")),
QtGui.QIcon.Selected,
QtGui.QIcon.Off)
item.setIcon(icon3)
self.tableWidgetAddressBook.setHorizontalHeaderItem(0, item)
item = QtGui.QTableWidgetItem()
@ -376,7 +407,11 @@ class Ui_MainWindow(object):
self.treeWidgetSubscriptions.setObjectName(_fromUtf8("treeWidgetSubscriptions"))
self.treeWidgetSubscriptions.resize(200, self.treeWidgetSubscriptions.height())
icon5 = QtGui.QIcon()
icon5.addPixmap(QtGui.QPixmap(_fromUtf8(":/newPrefix/images/subscriptions.png")), QtGui.QIcon.Selected, QtGui.QIcon.Off)
icon5.addPixmap(
QtGui.QPixmap(
_fromUtf8(":/newPrefix/images/subscriptions.png")),
QtGui.QIcon.Selected,
QtGui.QIcon.Off)
self.treeWidgetSubscriptions.headerItem().setIcon(0, icon5)
self.verticalSplitter_3.addWidget(self.treeWidgetSubscriptions)
self.pushButtonAddSubscription = QtGui.QPushButton(self.subscriptions)
@ -455,7 +490,11 @@ class Ui_MainWindow(object):
self.horizontalSplitter_4.setCollapsible(1, False)
self.gridLayout_3.addWidget(self.horizontalSplitter_4, 0, 0, 1, 1)
icon6 = QtGui.QIcon()
icon6.addPixmap(QtGui.QPixmap(_fromUtf8(":/newPrefix/images/subscriptions.png")), QtGui.QIcon.Normal, QtGui.QIcon.Off)
icon6.addPixmap(
QtGui.QPixmap(
_fromUtf8(":/newPrefix/images/subscriptions.png")),
QtGui.QIcon.Normal,
QtGui.QIcon.Off)
self.tabWidget.addTab(self.subscriptions, icon6, _fromUtf8(""))
self.chans = QtGui.QWidget()
self.chans.setObjectName(_fromUtf8("chans"))
@ -475,7 +514,11 @@ class Ui_MainWindow(object):
self.treeWidgetChans.setObjectName(_fromUtf8("treeWidgetChans"))
self.treeWidgetChans.resize(200, self.treeWidgetChans.height())
icon7 = QtGui.QIcon()
icon7.addPixmap(QtGui.QPixmap(_fromUtf8(":/newPrefix/images/can-icon-16px.png")), QtGui.QIcon.Selected, QtGui.QIcon.Off)
icon7.addPixmap(
QtGui.QPixmap(
_fromUtf8(":/newPrefix/images/can-icon-16px.png")),
QtGui.QIcon.Selected,
QtGui.QIcon.Off)
self.treeWidgetChans.headerItem().setIcon(0, icon7)
self.verticalSplitter_17.addWidget(self.treeWidgetChans)
self.pushButtonAddChan = QtGui.QPushButton(self.chans)
@ -554,7 +597,11 @@ class Ui_MainWindow(object):
self.horizontalSplitter_7.setCollapsible(1, False)
self.gridLayout_4.addWidget(self.horizontalSplitter_7, 0, 0, 1, 1)
icon8 = QtGui.QIcon()
icon8.addPixmap(QtGui.QPixmap(_fromUtf8(":/newPrefix/images/can-icon-16px.png")), QtGui.QIcon.Normal, QtGui.QIcon.Off)
icon8.addPixmap(
QtGui.QPixmap(
_fromUtf8(":/newPrefix/images/can-icon-16px.png")),
QtGui.QIcon.Normal,
QtGui.QIcon.Off)
self.tabWidget.addTab(self.chans, icon8, _fromUtf8(""))
self.blackwhitelist = Blacklist()
self.tabWidget.addTab(self.blackwhitelist, QtGui.QIcon(":/newPrefix/images/blacklist.png"), "")
@ -652,6 +699,8 @@ class Ui_MainWindow(object):
MainWindow.setTabOrder(self.textEditMessage, self.pushButtonAddSubscription)
def updateNetworkSwitchMenuLabel(self, dontconnect=None):
"""Restore last online/offline setting"""
if dontconnect is None:
dontconnect = BMConfigParser().safeGetBoolean(
'bitmessagesettings', 'dontconnect')
@ -662,6 +711,8 @@ class Ui_MainWindow(object):
)
def retranslateUi(self, MainWindow):
"""Re-translate the UI"""
MainWindow.setWindowTitle(_translate("MainWindow", "Bitmessage", None))
self.treeWidgetYourIdentities.headerItem().setText(0, _translate("MainWindow", "Identities", None))
self.pushButtonNewAddress.setText(_translate("MainWindow", "New Identity", None))
@ -691,19 +742,33 @@ class Ui_MainWindow(object):
self.label_3.setText(_translate("MainWindow", "Subject:", None))
self.label_2.setText(_translate("MainWindow", "From:", None))
self.label.setText(_translate("MainWindow", "To:", None))
#self.textEditMessage.setHtml("")
self.tabWidgetSend.setTabText(self.tabWidgetSend.indexOf(self.sendDirect), _translate("MainWindow", "Send ordinary Message", None))
# self.textEditMessage.setHtml("")
self.tabWidgetSend.setTabText(
self.tabWidgetSend.indexOf(
self.sendDirect),
_translate(
"MainWindow", "Send ordinary Message", None))
self.label_8.setText(_translate("MainWindow", "From:", None))
self.label_7.setText(_translate("MainWindow", "Subject:", None))
#self.textEditMessageBroadcast.setHtml("")
self.tabWidgetSend.setTabText(self.tabWidgetSend.indexOf(self.sendBroadcast), _translate("MainWindow", "Send Message to your Subscribers", None))
# self.textEditMessageBroadcast.setHtml("")
self.tabWidgetSend.setTabText(
self.tabWidgetSend.indexOf(
self.sendBroadcast),
_translate(
"MainWindow", "Send Message to your Subscribers", None))
self.pushButtonTTL.setText(_translate("MainWindow", "TTL:", None))
hours = 48
try:
hours = int(BMConfigParser().getint('bitmessagesettings', 'ttl')/60/60)
hours = int(BMConfigParser().getint('bitmessagesettings', 'ttl') / 60 / 60)
except:
pass
self.labelHumanFriendlyTTLDescription.setText(_translate("MainWindow", "%n hour(s)", None, QtCore.QCoreApplication.CodecForTr, hours))
self.labelHumanFriendlyTTLDescription.setText(
_translate(
"MainWindow",
"%n hour(s)",
None,
QtCore.QCoreApplication.CodecForTr,
hours))
self.pushButtonClear.setText(_translate("MainWindow", "Clear", None))
self.pushButtonSend.setText(_translate("MainWindow", "Send", None))
self.tabWidget.setTabText(self.tabWidget.indexOf(self.send), _translate("MainWindow", "Send", None))
@ -724,7 +789,11 @@ class Ui_MainWindow(object):
item.setText(_translate("MainWindow", "Subject", None))
item = self.tableWidgetInboxSubscriptions.horizontalHeaderItem(3)
item.setText(_translate("MainWindow", "Received", None))
self.tabWidget.setTabText(self.tabWidget.indexOf(self.subscriptions), _translate("MainWindow", "Subscriptions", None))
self.tabWidget.setTabText(
self.tabWidget.indexOf(
self.subscriptions),
_translate(
"MainWindow", "Subscriptions", None))
self.treeWidgetChans.headerItem().setText(0, _translate("MainWindow", "Chans", None))
self.pushButtonAddChan.setText(_translate("MainWindow", "Add Chan", None))
self.inboxSearchLineEditChans.setPlaceholderText(_translate("MainWindow", "Search", None))
@ -744,9 +813,17 @@ class Ui_MainWindow(object):
item.setText(_translate("MainWindow", "Received", None))
self.tabWidget.setTabText(self.tabWidget.indexOf(self.chans), _translate("MainWindow", "Chans", None))
self.blackwhitelist.retranslateUi()
self.tabWidget.setTabText(self.tabWidget.indexOf(self.blackwhitelist), _translate("blacklist", "Blacklist", None))
self.tabWidget.setTabText(
self.tabWidget.indexOf(
self.blackwhitelist),
_translate(
"blacklist", "Blacklist", None))
self.networkstatus.retranslateUi()
self.tabWidget.setTabText(self.tabWidget.indexOf(self.networkstatus), _translate("networkstatus", "Network Status", None))
self.tabWidget.setTabText(
self.tabWidget.indexOf(
self.networkstatus),
_translate(
"networkstatus", "Network Status", None))
self.menuFile.setTitle(_translate("MainWindow", "File", None))
self.menuSettings.setTitle(_translate("MainWindow", "Settings", None))
self.menuHelp.setTitle(_translate("MainWindow", "Help", None))
@ -759,19 +836,17 @@ class Ui_MainWindow(object):
self.actionSupport.setText(_translate("MainWindow", "Contact support", None))
self.actionAbout.setText(_translate("MainWindow", "About", None))
self.actionSettings.setText(_translate("MainWindow", "Settings", None))
self.actionRegenerateDeterministicAddresses.setText(_translate("MainWindow", "Regenerate deterministic addresses", None))
self.actionRegenerateDeterministicAddresses.setText(
_translate("MainWindow", "Regenerate deterministic addresses", None))
self.actionDeleteAllTrashedMessages.setText(_translate("MainWindow", "Delete all trashed messages", None))
self.actionJoinChan.setText(_translate("MainWindow", "Join / Create chan", None))
import bitmessage_icons_rc
if __name__ == "__main__":
import sys
app = QtGui.QApplication(sys.argv)
MainWindow = settingsmixin.SMainWindow()
ui = Ui_MainWindow()
ui.setupUi(MainWindow)
MainWindow.show()
sys.exit(app.exec_())
app = QtGui.QApplication(sys.argv)
ThisMainWindow = settingsmixin.SMainWindow()
ui = Ui_MainWindow()
ui.setupUi(ThisMainWindow)
ThisMainWindow.show()
sys.exit(app.exec_())

View File

@ -1,12 +1,26 @@
#!/usr/bin/env python2.7
g1itch commented 2018-06-08 14:49:32 +02:00 (Migrated from github.com)
Review

This is unused module

This is unused module
coffeedogs commented 2018-06-13 20:02:36 +02:00 (Migrated from github.com)
Review

Removed in PR#1271

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

View File

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

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

View File

@ -1,41 +1,45 @@
import base64
from binascii import hexlify
import hashlib
# pylint: disable=too-many-ancestors
"""
tcp.py
======
"""
from __future__ import absolute_import
import math
import time
from pprint import pprint
import socket
import struct
import random
import traceback
from addresses import calculateInventoryHash
from debug import logger
from helper_random import randomBytes
import helper_random
from inventory import Inventory
import knownnodes
from network.advanceddispatcher import AdvancedDispatcher
from network.bmproto import BMProtoError, BMProtoInsufficientDataError, BMProtoExcessiveDataError, BMProto
from network.bmobject import BMObject, BMObjectInsufficientPOWError, BMObjectInvalidDataError, BMObjectExpiredError, BMObjectUnwantedStreamError, BMObjectInvalidError, BMObjectAlreadyHaveError
from network.bmproto import BMProto
import network.connectionpool
from network.dandelion import Dandelion
from network.node import Node
import network.asyncore_pollchoose as asyncore
from network.proxy import Proxy, ProxyError, GeneralProxyError
from network.objectracker import ObjectTracker
from network.socks5 import Socks5Connection, Socks5Resolver, Socks5AuthError, Socks5Error
from network.socks4a import Socks4aConnection, Socks4aResolver, Socks4aError
from network.socks5 import Socks5Connection
from network.socks4a import Socks4aConnection
from network.tls import TLSDispatcher
import addresses
from bmconfigparser import BMConfigParser
from queues import invQueue, objectProcessorQueue, portCheckerQueue, UISignalQueue, receiveDataQueue
from queues import invQueue, UISignalQueue, receiveDataQueue
import shared
import state
import protocol
class TCPConnection(BMProto, TLSDispatcher):
class TCPConnection(BMProto, TLSDispatcher): # pylint: disable=too-many-instance-attributes
"""
.. todo:: Look to understand and/or fix the non-parent-init-called
"""
def __init__(self, address=None, sock=None):
BMProto.__init__(self, address=address, sock=sock)
self.verackReceived = False
@ -67,18 +71,27 @@ class TCPConnection(BMProto, TLSDispatcher):
self.connect(self.destination)
logger.debug("Connecting to %s:%i", self.destination.host, self.destination.port)
encodedAddr = protocol.encodeHost(self.destination.host)
if protocol.checkIPAddress(encodedAddr, True) and not protocol.checkSocksIP(self.destination.host):
self.local = True
else:
self.local = False
#shared.connectedHostsList[self.destination] = 0
ObjectTracker.__init__(self)
self.local = all([
protocol.checkIPAddress(encodedAddr, True),
not protocol.checkSocksIP(self.destination.host)
])
ObjectTracker.__init__(self) # pylint: disable=non-parent-init-called
self.bm_proto_reset()
self.set_state("bm_header", expectBytes=protocol.Header.size)
def antiIntersectionDelay(self, initial = False):
def antiIntersectionDelay(self, initial=False):
"""TBC"""
# estimated time for a small object to propagate across the whole network
delay = math.ceil(math.log(max(len(knownnodes.knownNodes[x]) for x in knownnodes.knownNodes) + 2, 20)) * (0.2 + invQueue.queueCount/2.0)
delay = math.ceil(
math.log(
max(
len(knownnodes.knownNodes[x])
for x in knownnodes.knownNodes
) + 2,
20
)
) * (0.2 + invQueue.queueCount / 2.0)
# take the stream with maximum amount of nodes
# +2 is to avoid problems with log(0) and log(1)
# 20 is avg connected nodes count
@ -93,12 +106,14 @@ class TCPConnection(BMProto, TLSDispatcher):
self.skipUntil = time.time() + delay
def state_connection_fully_established(self):
"""TBC"""
self.set_connection_fully_established()
self.set_state("bm_header")
self.bm_proto_reset()
return True
def set_connection_fully_established(self):
"""TBC"""
if not self.isOutbound and not self.local:
shared.clientHasReceivedIncomingConnections = True
UISignalQueue.put(('setStatusIcon', 'green'))
@ -113,50 +128,50 @@ class TCPConnection(BMProto, TLSDispatcher):
self.sendBigInv()
def sendAddr(self):
"""TBC"""
# We are going to share a maximum number of 1000 addrs (per overlapping
# stream) with our peer. 500 from overlapping streams, 250 from the
# left child stream, and 250 from the right child stream.
maxAddrCount = BMConfigParser().safeGetInt("bitmessagesettings", "maxaddrperstreamsend", 500)
# init
addressCount = 0
payload = b''
templist = []
addrs = {}
for stream in self.streams:
with knownnodes.knownNodesLock:
if len(knownnodes.knownNodes[stream]) > 0:
if knownnodes.knownNodes[stream]:
filtered = {k: v for k, v in knownnodes.knownNodes[stream].items()
if v["lastseen"] > (int(time.time()) - shared.maximumAgeOfNodesThatIAdvertiseToOthers)}
if v["lastseen"] > (int(time.time()) - shared.maximumAgeOfNodesThatIAdvertiseToOthers)}
elemCount = len(filtered)
if elemCount > maxAddrCount:
elemCount = maxAddrCount
# only if more recent than 3 hours
addrs[stream] = helper_random.randomsample(filtered.items(), elemCount)
# sent 250 only if the remote isn't interested in it
if len(knownnodes.knownNodes[stream * 2]) > 0 and stream not in self.streams:
filtered = {k: v for k, v in knownnodes.knownNodes[stream*2].items()
if v["lastseen"] > (int(time.time()) - shared.maximumAgeOfNodesThatIAdvertiseToOthers)}
if knownnodes.knownNodes[stream * 2] and stream not in self.streams:
filtered = {k: v for k, v in knownnodes.knownNodes[stream * 2].items()
if v["lastseen"] > (int(time.time()) - shared.maximumAgeOfNodesThatIAdvertiseToOthers)}
elemCount = len(filtered)
if elemCount > maxAddrCount / 2:
elemCount = int(maxAddrCount / 2)
addrs[stream * 2] = helper_random.randomsample(filtered.items(), elemCount)
if len(knownnodes.knownNodes[(stream * 2) + 1]) > 0 and stream not in self.streams:
filtered = {k: v for k, v in knownnodes.knownNodes[stream*2+1].items()
if v["lastseen"] > (int(time.time()) - shared.maximumAgeOfNodesThatIAdvertiseToOthers)}
if knownnodes.knownNodes[(stream * 2) + 1] and stream not in self.streams:
filtered = {k: v for k, v in knownnodes.knownNodes[stream * 2 + 1].items()
if v["lastseen"] > (int(time.time()) - shared.maximumAgeOfNodesThatIAdvertiseToOthers)}
elemCount = len(filtered)
if elemCount > maxAddrCount / 2:
elemCount = int(maxAddrCount / 2)
addrs[stream * 2 + 1] = helper_random.randomsample(filtered.items(), elemCount)
g1itch commented 2018-06-08 14:55:44 +02:00 (Migrated from github.com)
Review

Oh my eyes

Oh my eyes
coffeedogs commented 2018-06-13 20:03:43 +02:00 (Migrated from github.com)
Review

Fixed in PR#1271

Fixed in PR#1271
for substream in addrs.keys():
for substream in addrs:
for peer, params in addrs[substream]:
templist.append((substream, peer, params["lastseen"]))
if len(templist) > 0:
if templist:
self.append_write_buf(BMProto.assembleAddr(templist))
def sendBigInv(self):
"""TBC"""
def sendChunk():
"""TBC"""
if objectCount == 0:
return
logger.debug('Sending huge inv message with %i objects to just this one peer', objectCount)
@ -172,13 +187,12 @@ class TCPConnection(BMProto, TLSDispatcher):
if Dandelion().hasHash(objHash):
continue
bigInvList[objHash] = 0
#self.objectsNewToThem[objHash] = time.time()
objectCount = 0
payload = b''
# Now let us start appending all of these hashes together. They will be
# sent out in a big inv message to our new peer.
for hash, storedValue in bigInvList.items():
payload += hash
for obj_hash, _ in bigInvList.items():
payload += obj_hash
objectCount += 1
if objectCount >= BMProto.maxObjectCount:
sendChunk()
@ -189,20 +203,26 @@ class TCPConnection(BMProto, TLSDispatcher):
sendChunk()
def handle_connect(self):
"""TBC"""
try:
AdvancedDispatcher.handle_connect(self)
except socket.error as e:
if e.errno in asyncore._DISCONNECTED:
logger.debug("%s:%i: Connection failed: %s" % (self.destination.host, self.destination.port, str(e)))
if e.errno in asyncore._DISCONNECTED: # pylint: disable=protected-access
logger.debug("%s:%i: Connection failed: %s", self.destination.host, self.destination.port, str(e))
return
self.nodeid = randomBytes(8)
self.append_write_buf(protocol.assembleVersionMessage(self.destination.host, self.destination.port, \
network.connectionpool.BMConnectionPool().streams, False, nodeid=self.nodeid))
#print "%s:%i: Sending version" % (self.destination.host, self.destination.port)
self.append_write_buf(
protocol.assembleVersionMessage(
self.destination.host,
self.destination.port,
network.connectionpool.BMConnectionPool().streams,
False,
nodeid=self.nodeid))
self.connectedAt = time.time()
receiveDataQueue.put(self.destination)
def handle_read(self):
"""TBC"""
TLSDispatcher.handle_read(self)
if self.isOutbound and self.fullyEstablished:
for s in self.streams:
@ -214,9 +234,11 @@ class TCPConnection(BMProto, TLSDispatcher):
receiveDataQueue.put(self.destination)
def handle_write(self):
"""TBC"""
TLSDispatcher.handle_write(self)
def handle_close(self):
"""TBC"""
if self.isOutbound and not self.fullyEstablished:
knownnodes.decreaseRating(self.destination)
if self.fullyEstablished:
@ -227,37 +249,55 @@ class TCPConnection(BMProto, TLSDispatcher):
class Socks5BMConnection(Socks5Connection, TCPConnection):
"""TBC"""
def __init__(self, address):
Socks5Connection.__init__(self, address=address)
TCPConnection.__init__(self, address=address, sock=self.socket)
self.set_state("init")
def state_proxy_handshake_done(self):
"""TBC"""
Socks5Connection.state_proxy_handshake_done(self)
self.nodeid = randomBytes(8)
self.append_write_buf(protocol.assembleVersionMessage(self.destination.host, self.destination.port, \
network.connectionpool.BMConnectionPool().streams, False, nodeid=self.nodeid))
self.append_write_buf(
protocol.assembleVersionMessage(
self.destination.host,
self.destination.port,
network.connectionpool.BMConnectionPool().streams,
False,
nodeid=self.nodeid))
self.set_state("bm_header", expectBytes=protocol.Header.size)
return True
class Socks4aBMConnection(Socks4aConnection, TCPConnection):
"""TBC"""
def __init__(self, address):
Socks4aConnection.__init__(self, address=address)
TCPConnection.__init__(self, address=address, sock=self.socket)
self.set_state("init")
def state_proxy_handshake_done(self):
"""TBC"""
Socks4aConnection.state_proxy_handshake_done(self)
self.nodeid = randomBytes(8)
self.append_write_buf(protocol.assembleVersionMessage(self.destination.host, self.destination.port, \
network.connectionpool.BMConnectionPool().streams, False, nodeid=self.nodeid))
self.append_write_buf(
protocol.assembleVersionMessage(
self.destination.host,
self.destination.port,
network.connectionpool.BMConnectionPool().streams,
False,
nodeid=self.nodeid))
self.set_state("bm_header", expectBytes=protocol.Header.size)
return True
class TCPServer(AdvancedDispatcher):
def __init__(self, host='127.0.0.1', port=8444):
"""TBC"""
def __init__(self, host='127.0.0.1', port=8444): # pylint: disable=redefined-outer-name
if not hasattr(self, '_map'):
AdvancedDispatcher.__init__(self)
self.create_socket(socket.AF_INET, socket.SOCK_STREAM)
@ -280,20 +320,22 @@ class TCPServer(AdvancedDispatcher):
self.listen(5)
def is_bound(self):
"""TBC"""
try:
return self.bound
except AttributeError:
return False
def handle_accept(self):
"""TBC"""
pair = self.accept()
if pair is not None:
sock, addr = pair
sock, _ = pair
state.ownAddresses[state.Peer(sock.getsockname()[0], sock.getsockname()[1])] = True
if len(network.connectionpool.BMConnectionPool().inboundConnections) + \
len(network.connectionpool.BMConnectionPool().outboundConnections) > \
BMConfigParser().safeGetInt("bitmessagesettings", "maxtotalconnections") + \
BMConfigParser().safeGetInt("bitmessagesettings", "maxbootstrapconnections") + 10:
len(network.connectionpool.BMConnectionPool().outboundConnections) > \
BMConfigParser().safeGetInt("bitmessagesettings", "maxtotalconnections") + \
BMConfigParser().safeGetInt("bitmessagesettings", "maxbootstrapconnections") + 10:
# 10 is a sort of buffer, in between it will go through the version handshake
# and return an error to the peer
logger.warning("Server full, dropping connection")
@ -310,17 +352,7 @@ if __name__ == "__main__":
for host in (("127.0.0.1", 8448),):
direct = TCPConnection(host)
while len(asyncore.socket_map) > 0:
while asyncore.socket_map:
print "loop, state = %s" % (direct.state)
asyncore.loop(timeout=10, count=1)
continue
proxy = Socks5BMConnection(host)
while len(asyncore.socket_map) > 0:
# print "loop, state = %s" % (proxy.state)
asyncore.loop(timeout=10, count=1)
proxy = Socks4aBMConnection(host)
while len(asyncore.socket_map) > 0:
# print "loop, state = %s" % (proxy.state)
asyncore.loop(timeout=10, count=1)

View File

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

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

View File

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