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