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

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

View File

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

View File

@ -1,19 +1,24 @@
# pylint: disable=too-many-locals,too-many-lines,no-self-use,too-many-public-methods,too-many-branches
# pylint: disable=too-many-statements
"""
# Copyright (c) 2012-2016 Jonathan Warren
# Copyright (c) 2012-2018 The Bitmessage developers
"""
This is not what you run to run the Bitmessage API. Instead, enable the API
( https://bitmessage.org/wiki/API ) and optionally enable daemon mode
( https://bitmessage.org/wiki/Daemon ) then run bitmessagemain.py.
"""
from __future__ import absolute_import
import base64
import hashlib
import json
from struct import pack
import time
g1itch commented 2018-06-08 14:59:13 +02:00 (Migrated from github.com)
Review

Why?

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

In the wrong order: Continued in PR#1271

In the wrong order: Continued in PR#1271
from binascii import hexlify, unhexlify
from SimpleXMLRPCServer import SimpleXMLRPCRequestHandler, SimpleXMLRPCServer
from struct import pack
import shared
from addresses import (
@ -43,6 +48,8 @@ str_chan = '[chan]'
class APIError(Exception):
"""APIError exception class"""
def __init__(self, error_number, error_message):
super(APIError, self).__init__()
self.error_number = error_number
@ -53,26 +60,34 @@ class APIError(Exception):
class StoppableXMLRPCServer(SimpleXMLRPCServer):
"""A SimpleXMLRPCServer that honours state.shutdown"""
allow_reuse_address = True
def serve_forever(self):
"""Start the SimpleXMLRPCServer"""
# pylint: disable=arguments-differ
while state.shutdown == 0:
self.handle_request()
# This is one of several classes that constitute the API
# This class was written by Vaibhav Bhatia.
# Modified by Jonathan Warren (Atheros).
# http://code.activestate.com/recipes/501148-xmlrpc-serverclient-which-does-cookie-handling-and/
class MySimpleXMLRPCRequestHandler(SimpleXMLRPCRequestHandler):
"""
This is one of several classes that constitute the API
This class was written by Vaibhav Bhatia. Modified by Jonathan Warren (Atheros).
http://code.activestate.com/recipes/501148-xmlrpc-serverclient-which-does-cookie-handling-and/
"""
def do_POST(self):
# Handles the HTTP POST request.
# Attempts to interpret all HTTP POST requests as XML-RPC calls,
# which are forwarded to the server's _dispatch method for handling.
"""
Handles the HTTP POST request.
# Note: this method is the same as in SimpleXMLRPCRequestHandler,
# just hacked to handle cookies
Attempts to interpret all HTTP POST requests as XML-RPC calls,
which are forwarded to the server's _dispatch method for handling.
Note: this method is the same as in SimpleXMLRPCRequestHandler,
just hacked to handle cookies
"""
# Check that the path is legal
if not self.is_rpc_path_valid():
@ -98,7 +113,7 @@ class MySimpleXMLRPCRequestHandler(SimpleXMLRPCRequestHandler):
# SimpleXMLRPCDispatcher. To maintain backwards compatibility,
# check to see if a subclass implements _dispatch and dispatch
# using that method if present.
response = self.server._marshaled_dispatch(
response = self.server._marshaled_dispatch( # pylint: disable=protected-access
data, getattr(self, '_dispatch', None)
)
except: # This should only happen if the module is buggy
@ -125,22 +140,21 @@ class MySimpleXMLRPCRequestHandler(SimpleXMLRPCRequestHandler):
self.connection.shutdown(1)
def APIAuthenticateClient(self):
"""Predicate to check for valid API credentials in the request header"""
if 'Authorization' in self.headers:
# handle Basic authentication
enctype, encstr = self.headers.get('Authorization').split()
_, encstr = self.headers.get('Authorization').split()
emailid, password = encstr.decode('base64').split(':')
return (
emailid ==
BMConfigParser().get('bitmessagesettings', 'apiusername')
and password ==
BMConfigParser().get('bitmessagesettings', 'apipassword')
emailid == BMConfigParser().get('bitmessagesettings', 'apiusername') and
password == BMConfigParser().get('bitmessagesettings', 'apipassword')
)
else:
logger.warning(
'Authentication failed because header lacks'
' Authentication field')
time.sleep(2)
return False
return False
@ -155,6 +169,7 @@ class MySimpleXMLRPCRequestHandler(SimpleXMLRPCRequestHandler):
22, "Decode error - %s. Had trouble while decoding string: %r"
% (e, text)
)
return None
def _verifyAddress(self, address):
status, addressVersionNumber, streamNumber, ripe = \
@ -170,15 +185,10 @@ class MySimpleXMLRPCRequestHandler(SimpleXMLRPCRequestHandler):
if status == 'invalidcharacters':
raise APIError(9, 'Invalid characters in address: ' + address)
if status == 'versiontoohigh':
raise APIError(
10,
'Address version number too high (or zero) in address: '
+ address
)
raise APIError(10, 'Address version number too high (or zero) in address: ' + address)
if status == 'varintmalformed':
raise APIError(26, 'Malformed varint in address: ' + address)
raise APIError(
7, 'Could not decode address: %s : %s' % (address, status))
raise APIError(7, 'Could not decode address: %s : %s' % (address, status))
if addressVersionNumber < 2 or addressVersionNumber > 4:
raise APIError(
11, 'The address version number currently must be 2, 3 or 4.'
@ -195,9 +205,11 @@ class MySimpleXMLRPCRequestHandler(SimpleXMLRPCRequestHandler):
# Request Handlers
def HandleListAddresses(self, method):
"""Handle a request to list addresses"""
data = '{"addresses":['
for addressInKeysFile in BMConfigParser().addresses():
status, addressVersionNumber, streamNumber, hash01 = decodeAddress(
status, addressVersionNumber, streamNumber, hash01 = decodeAddress( # pylint: disable=unused-variable
addressInKeysFile)
if len(data) > 20:
data += ','
@ -220,6 +232,8 @@ class MySimpleXMLRPCRequestHandler(SimpleXMLRPCRequestHandler):
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)
@ -1105,13 +1201,14 @@ class MySimpleXMLRPCRequestHandler(SimpleXMLRPCRequestHandler):
pubkeyReadPosition += 8
else:
pubkeyReadPosition += 4
# pylint: disable=unused-variable
addressVersion, addressVersionLength = decodeVarint(
payload[pubkeyReadPosition:pubkeyReadPosition + 10])
pubkeyReadPosition += addressVersionLength
pubkeyStreamNumber = decodeVarint(
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.
@ -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:
@ -1180,6 +1281,8 @@ class MySimpleXMLRPCRequestHandler(SimpleXMLRPCRequestHandler):
}, 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!')
@ -1193,26 +1296,38 @@ class MySimpleXMLRPCRequestHandler(SimpleXMLRPCRequestHandler):
}, indent=4, separators=(',', ': '))
def HandleHelloWorld(self, params):
"""Test two string params"""
a, b = params
return a + '-' + b
def HandleAdd(self, params):
"""Test two numeric params"""
a, b = params
return a + b
def HandleStatusBar(self, params):
"""Handle a request to update the status bar"""
message, = params
queues.UISignalQueue.put(('updateStatusBar', message))
def HandleDeleteAndVacuum(self, params):
"""Handle a request to run the deleteandvacuum stored procedure"""
if not params:
sqlStoredProcedure('deleteandvacuume')
return 'done'
return None
def HandleShutdown(self, params):
"""Handle a request to huutdown the client"""
if not params:
shutdown.doCleanShutdown()
return 'done'
return None
handlers = {}
handlers['helloWorld'] = HandleHelloWorld
@ -1279,6 +1394,7 @@ class MySimpleXMLRPCRequestHandler(SimpleXMLRPCRequestHandler):
return self.handlers[method](self, params)
def _dispatch(self, method, params):
# pylint: disable=attribute-defined-outside-init
self.cookies = []
validuser = self.APIAuthenticateClient()

View File

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

Why are you moving copyrights to docstring?

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

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

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

This will probably break script execution.

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

View File

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

This changes break portable usage.

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

Continued in PR#1271

Continued in PR#1271
# pylint: disable=unused-argument
if n is None:
return QtGui.QApplication.translate(context, text, disambig, _encoding)
else:
return QtGui.QApplication.translate(context, text, disambig, _encoding, n)
except AttributeError:
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)
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))
@ -692,18 +743,32 @@ class Ui_MainWindow(object):
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.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.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)
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_())

View File

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

This is unused module

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

Removed in PR#1271

Removed in PR#1271
# pylint: disable=no-self-use
"""
newaddresswizard.py
===================
"""
import sys
import time
from PyQt4 import QtCore, QtGui
class NewAddressWizardIntroPage(QtGui.QWizardPage):
"""The introduction page for the new address wizard"""
def __init__(self):
super(QtGui.QWizardPage, self).__init__()
super(NewAddressWizardIntroPage, self).__init__()
self.setTitle("Creating a new address")
label = QtGui.QLabel("This wizard will help you create as many addresses as you like. Indeed, creating and abandoning addresses is encouraged.\n\n"
label = QtGui.QLabel(
"This wizard will help you create as many addresses as you like."
" Indeed, creating and abandoning addresses is encouraged.\n\n"
"What type of address would you like? Would you like to send emails or not?\n"
"You can still change your mind later, and register/unregister with an email service provider.\n\n")
label.setWordWrap(True)
@ -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
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. "
label = QtGui.QLabel(
"<html><head/><body><p>You may generate addresses by using either random numbers or by using a passphrase."
" If you use a passphrase, the address is called a &quot;deterministic&quot; address."
"The \'Random Number\' option is selected by default but deterministic addresses have several pros and cons:</p>"
"<table border=0><tr><td><span style=\" font-weight:600;\">Pros:</span></td><td><span style=\" font-weight:600;\">Cons:</span></td></tr>"
" 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>")
" 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
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,7 +98,8 @@ 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)")
@ -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,12 +183,16 @@ 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 "
@ -168,21 +200,22 @@ class NewAddressWizardEmailProviderPage(QtGui.QWizardPage):
"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
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
self.emit(self.signal, i)
self.emit(self.signal, 101)
# self.terminate()
if __name__ == '__main__':
import sys
app = QtGui.QApplication(sys.argv)
wizard = Ui_NewAddressWizard(["a", "b", "c", "d"])
if (wizard.exec_()):
if wizard.exec_():
print "Email: " + ("yes" if wizard.field("emailAsWell").toBool() else "no")
print "BM: " + ("yes" if wizard.field("onlyBM").toBool() else "no")
else:

View File

@ -1,7 +1,9 @@
# -*- Mode: Python -*-
# Id: asyncore.py,v 2.51 2000/09/07 22:29:26 rushing Exp
# Author: Sam Rushing <rushing@nightmare.com>
# pylint: disable=too-many-statements,too-many-branches,no-self-use,too-many-lines,attribute-defined-outside-init
# pylint: disable=global-statement
"""
# ======================================================================
# Copyright 1996 by Sam Rushing
#
@ -25,7 +27,7 @@
# CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
# ======================================================================
"""Basic infrastructure for asynchronous socket service clients and servers.
Basic infrastructure for asynchronous socket service clients and servers.
There are only two ways to have a program on a single processor do "more
than one thing at a time". Multi-threaded programming is the simplest and
@ -46,17 +48,13 @@ 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, \
@ -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,6 +92,7 @@ try:
except NameError:
socket_map = {}
def _strerror(err):
try:
return os.strerror(err)
@ -99,9 +101,12 @@ def _strerror(err):
return errorcode[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,8 +291,11 @@ 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:
@ -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,8 +475,10 @@ 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:
@ -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:
@ -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,19 +624,23 @@ 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,
@ -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,16 +673,19 @@ 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)
@ -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,7 +740,6 @@ class dispatcher:
# a read condition, and having recv() return 0.
self.handle_close()
return b''
else:
return data
except socket.error as why:
# winsock sometimes raises ENOTCONN
@ -678,6 +752,7 @@ class dispatcher:
raise
def close(self):
"""Close connection"""
self.connected = False
self.accepting = False
self.connecting = False
@ -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:
@ -782,64 +864,81 @@ class dispatcher:
)
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
@ -855,11 +954,15 @@ def compact_traceback():
# 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,24 +994,31 @@ 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):
@ -919,14 +1030,19 @@ if os.name == 'posix':
write = send
def close(self):
"""Fake close()"""
os.close(self.fd)
def fileno(self):
"""Fake fileno()"""
return self.fd
class file_dispatcher(dispatcher):
"""A dispatcher for file_wrapper objects"""
def __init__(self, fd, map=None):
# pylint: disable=redefined-builtin
dispatcher.__init__(self, None, map)
self.connected = True
try:
@ -940,6 +1056,7 @@ if os.name == 'posix':
fcntl.fcntl(fd, fcntl.F_SETFL, flags)
def set_file(self, fd):
"""Set file"""
self.socket = file_wrapper(fd)
self._fileno = self.socket.fileno()
self.add_channel()

View File

@ -1,6 +1,12 @@
# pylint: disable=too-many-return-statements,too-many-public-methods,attribute-defined-outside-init,too-many-branches
# pylint: disable=too-many-instance-attributes,too-many-statements
"""
The Bitmessage Protocol
=======================
"""
import base64
import hashlib
import random
import socket
import struct
import time
@ -16,7 +22,7 @@ from network.bmobject import BMObject, BMObjectInsufficientPOWError, BMObjectInv
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")
@ -134,16 +155,21 @@ class BMProto(AdvancedDispatcher, ObjectTracker):
return True
def decode_payload_string(self, 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]))
@ -160,18 +186,24 @@ 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
"""
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":
@ -185,6 +217,7 @@ class BMProto(AdvancedDispatcher, ObjectTracker):
if char == "Q":
self.payloadOffset += 8
return struct.unpack(">Q", self.payload[self.payloadOffset - 8:self.payloadOffset])[0]
return None
size = None
isArray = False
@ -197,13 +230,6 @@ 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"):
@ -212,12 +238,11 @@ class BMProto(AdvancedDispatcher, ObjectTracker):
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
# 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)
@ -436,15 +483,18 @@ class BMProto(AdvancedDispatcher, ObjectTracker):
logger.debug("user agent: %s", self.userAgent)
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,22 +507,25 @@ 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."))
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,
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)
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)
shared.timeOffsetWrongCount += 1
@ -480,16 +533,17 @@ class BMProto(AdvancedDispatcher, ObjectTracker):
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."))
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."))
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
@ -510,7 +564,9 @@ class BMProto(AdvancedDispatcher, ObjectTracker):
str(self.destination))
return False
if network.connectionpool.BMConnectionPool().isAlreadyConnected(self.nonce):
self.append_write_buf(protocol.assembleErrorMessage(fatal=2,
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))
@ -520,6 +576,7 @@ class BMProto(AdvancedDispatcher, ObjectTracker):
@staticmethod
def assembleAddr(peerList):
"""iBuild up a packed address"""
if isinstance(peerList, state.Peer):
peerList = (peerList)
if not peerList:
@ -541,6 +598,7 @@ class BMProto(AdvancedDispatcher, ObjectTracker):
@staticmethod
def stopDownloadingObject(hashId, forwardAnyway=False):
"""Stop downloading an object"""
for connection in network.connectionpool.BMConnectionPool().inboundConnections.values() + \
network.connectionpool.BMConnectionPool().outboundConnections.values():
try:
@ -559,6 +617,7 @@ class BMProto(AdvancedDispatcher, ObjectTracker):
pass
def handle_close(self):
"""Handle close"""
self.set_state("close")
if not (self.accepting or self.connecting or self.connected):
# already disconnected

View File

@ -1,41 +1,45 @@
import base64
from binascii import hexlify
import hashlib
# pylint: disable=too-many-ancestors
"""
tcp.py
======
"""
from __future__ import absolute_import
import math
import time
from pprint import pprint
import socket
import struct
import random
import traceback
from addresses import calculateInventoryHash
from debug import logger
from helper_random import randomBytes
import helper_random
from inventory import Inventory
import knownnodes
from network.advanceddispatcher import AdvancedDispatcher
from network.bmproto import BMProtoError, BMProtoInsufficientDataError, BMProtoExcessiveDataError, BMProto
from network.bmobject import BMObject, BMObjectInsufficientPOWError, BMObjectInvalidDataError, BMObjectExpiredError, BMObjectUnwantedStreamError, BMObjectInvalidError, BMObjectAlreadyHaveError
from network.bmproto import BMProto
import network.connectionpool
from network.dandelion import Dandelion
from network.node import Node
import network.asyncore_pollchoose as asyncore
from network.proxy import Proxy, ProxyError, GeneralProxyError
from network.objectracker import ObjectTracker
from network.socks5 import Socks5Connection, Socks5Resolver, Socks5AuthError, Socks5Error
from network.socks4a import Socks4aConnection, Socks4aResolver, Socks4aError
from network.socks5 import Socks5Connection
from network.socks4a import Socks4aConnection
from network.tls import TLSDispatcher
import addresses
from bmconfigparser import BMConfigParser
from queues import invQueue, objectProcessorQueue, portCheckerQueue, UISignalQueue, receiveDataQueue
from queues import invQueue, UISignalQueue, receiveDataQueue
import shared
import state
import protocol
class TCPConnection(BMProto, TLSDispatcher):
class TCPConnection(BMProto, TLSDispatcher): # pylint: disable=too-many-instance-attributes
"""
.. todo:: Look to understand and/or fix the non-parent-init-called
"""
def __init__(self, address=None, sock=None):
BMProto.__init__(self, address=address, sock=sock)
self.verackReceived = False
@ -67,18 +71,27 @@ class TCPConnection(BMProto, TLSDispatcher):
self.connect(self.destination)
logger.debug("Connecting to %s:%i", self.destination.host, self.destination.port)
encodedAddr = protocol.encodeHost(self.destination.host)
if protocol.checkIPAddress(encodedAddr, True) and not protocol.checkSocksIP(self.destination.host):
self.local = True
else:
self.local = False
#shared.connectedHostsList[self.destination] = 0
ObjectTracker.__init__(self)
self.local = all([
protocol.checkIPAddress(encodedAddr, True),
not protocol.checkSocksIP(self.destination.host)
])
ObjectTracker.__init__(self) # pylint: disable=non-parent-init-called
self.bm_proto_reset()
self.set_state("bm_header", expectBytes=protocol.Header.size)
def antiIntersectionDelay(self, initial=False):
"""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,20 +128,18 @@ 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)}
elemCount = len(filtered)
@ -135,28 +148,30 @@ class TCPConnection(BMProto, TLSDispatcher):
# 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:
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:
if knownnodes.knownNodes[(stream * 2) + 1] and stream not in self.streams:
filtered = {k: v for k, v in knownnodes.knownNodes[stream * 2 + 1].items()
if v["lastseen"] > (int(time.time()) - shared.maximumAgeOfNodesThatIAdvertiseToOthers)}
elemCount = len(filtered)
if elemCount > maxAddrCount / 2:
elemCount = int(maxAddrCount / 2)
addrs[stream * 2 + 1] = helper_random.randomsample(filtered.items(), elemCount)
g1itch commented 2018-06-08 14:55:44 +02:00 (Migrated from github.com)
Review

Oh my eyes

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

Fixed in PR#1271

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

View File

@ -1,33 +1,42 @@
#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)
@ -35,26 +44,31 @@ def _set_idle():
# 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
@ -110,15 +125,28 @@ def _doCPoW(target, initialHash):
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)
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:
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,16 +245,14 @@ 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:
@ -225,7 +278,6 @@ def run(target, initialHash):
raise
except:
logger.error("Fast PoW got exception:", exc_info=True)
pass #fallback
try:
return _doSafePoW(target, initialHash)
except StopIteration:
@ -233,16 +285,23 @@ def run(target, initialHash):
except:
pass # fallback
def resetPoW():
"""TBC"""
openclpow.initCL()
# init
def init():
global bitmsglib, bso, bmpow
"""TBC"""
# pylint: disable=global-statement
global bitmsglib, bmpow
openclpow.initCL()
if "win32" == sys.platform:
if sys.platform == "win32":
if ctypes.sizeof(ctypes.c_voidp) == 4:
bitmsglib = 'bitmsghash32.dll'
else:

View File

@ -1,4 +1,6 @@
"""SocksiPy - Python SOCKS module.
# pylint: disable=too-many-arguments,global-statement,too-many-branches
"""
SocksiPy - Python SOCKS module.
Version 1.00
Copyright 2006 Dan-Haim. All rights reserved.
@ -28,10 +30,6 @@ OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMANGE.
This module provides a standard socket-like interface for Python
for tunneling connections through SOCKS proxies.
"""
"""
Minor modifications made by Christopher Gilbert (http://motomastyle.com/)
for use in PyLoris (http://pyloris.sourceforge.net/)
@ -42,7 +40,7 @@ mainly to merge bug fixes found in Sourceforge
import socket
import struct
import sys
PROXY_TYPE_SOCKS4 = 1
PROXY_TYPE_SOCKS5 = 2
@ -51,12 +49,36 @@ 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",
@ -92,6 +114,7 @@ _socks4errors = ("request granted",
"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]]]])
Sets a default proxy which all further socksocket objects will use,
@ -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)
@ -139,7 +165,8 @@ class socksocket(socket.socket):
raise GeneralProxyError((6, "timed out"))
while len(data) < count:
d = self.recv(count - len(data))
if not d: raise GeneralProxyError((0, "connection closed unexpectedly"))
if not d:
raise GeneralProxyError((0, "connection closed unexpectedly"))
data = data + d
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
@ -251,7 +282,7 @@ class socksocket(socket.socket):
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)
@ -284,7 +315,7 @@ class socksocket(socket.socket):
else:
self.close()
raise GeneralProxyError((1, _generalerrors[1]))
boundport = struct.unpack(">H", self.__recvall(2))[0]
_ = struct.unpack(">H", self.__recvall(2))[0]
return ip
def getproxysockname(self):
@ -307,6 +338,7 @@ class socksocket(socket.socket):
return self.__proxypeername
def getproxytype(self):
"""Get the proxy type"""
return self.__proxy[0]
def __negotiatesocks4(self, destaddr, destport):
@ -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
@ -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,14 +465,14 @@ 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))
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
@ -444,14 +490,15 @@ class socksocket(socket.socket):
raise GeneralProxyError((9, _generalerrors[9]))
raise
self.__negotiatehttp(destpair[0], destpair[1])
elif self.__proxy[0] == None:
elif self.__proxy[0] is None:
_orgsocket.connect(self, (destpair[0], destpair[1]))
else:
raise GeneralProxyError((4, _generalerrors[4]))
def resolve(self, host):
"""TBC"""
if self.__proxy[0] == PROXY_TYPE_SOCKS5:
if self.__proxy[2] != None:
if self.__proxy[2] is not None:
portnum = self.__proxy[2]
else:
portnum = 1080

View File

@ -1,21 +1,33 @@
# A simple upnp module to forward port for BitMessage
# Reference: http://mattscodecave.com/posts/using-python-and-upnp-to-forward-a-port
# pylint: disable=too-many-statements,too-many-branches,protected-access,no-self-use
"""
A simple upnp module to forward port for BitMessage
Reference: http://mattscodecave.com/posts/using-python-and-upnp-to-forward-a-port
"""
from __future__ import absolute_import
import httplib
from random import randint
import socket
from struct import unpack, pack
from struct import unpack
import threading
import time
import urllib2
from urlparse import urlparse
from xml.dom.minidom import Document, parseString
from bmconfigparser import BMConfigParser
from debug import logger
from network.connectionpool import BMConnectionPool
from helper_threading import *
from helper_threading import StoppableThread
import queues
import shared
import state
import tr
def createRequestXML(service, action, arguments=None):
from xml.dom.minidom import Document
"""Router UPnP requests are XML formatted"""
doc = Document()
@ -63,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
@ -112,8 +126,18 @@ class Router:
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)),
@ -125,11 +149,13 @@ class Router:
('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)),
@ -139,14 +165,15 @@ class Router:
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',
@ -164,14 +191,17 @@ 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))
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
@ -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)
@ -263,13 +297,16 @@ class uPnPThread(threading.Thread, StoppableThread):
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" + \
@ -283,11 +320,11 @@ 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
@ -295,7 +332,12 @@ class uPnPThread(threading.Thread, StoppableThread):
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')