Inherited APIError from xmlrpclib.Fault.

From now on any errors are raised.
This commit is contained in:
Dmitri Bogomolov 2018-10-25 12:52:16 +03:00
parent 25abf66f1d
commit 2142888cbe
Signed by untrusted user: g1itch
GPG Key ID: 720A756F18DEED13
2 changed files with 55 additions and 40 deletions

View File

@ -20,6 +20,7 @@ import time
from binascii import hexlify, unhexlify from binascii import hexlify, unhexlify
from SimpleXMLRPCServer import SimpleXMLRPCRequestHandler, SimpleXMLRPCServer from SimpleXMLRPCServer import SimpleXMLRPCRequestHandler, SimpleXMLRPCServer
from struct import pack from struct import pack
import xmlrpclib
import defaults import defaults
import helper_inbox import helper_inbox
@ -49,16 +50,10 @@ str_chan = '[chan]'
str_broadcast_subscribers = '[Broadcast subscribers]' str_broadcast_subscribers = '[Broadcast subscribers]'
class APIError(Exception): class APIError(xmlrpclib.Fault):
"""APIError exception class""" """APIError exception class"""
def __init__(self, error_number, error_message):
super(APIError, self).__init__()
self.error_number = error_number
self.error_message = error_message
def __str__(self): def __str__(self):
return "API Error %04i: %s" % (self.error_number, self.error_message) return "API Error %04i: %s" % (self.faultCode, self.faultString)
class StoppableXMLRPCServer(SimpleXMLRPCServer): class StoppableXMLRPCServer(SimpleXMLRPCServer):
@ -107,7 +102,7 @@ class singleAPI(StoppableThread):
(BMConfigParser().get( (BMConfigParser().get(
'bitmessagesettings', 'apiinterface'), 'bitmessagesettings', 'apiinterface'),
port), port),
MySimpleXMLRPCRequestHandler, True, True) BMXMLRPCRequestHandler, True, True)
except socket.error as e: except socket.error as e:
if e.errno in (errno.EADDRINUSE, errno.WSAEADDRINUSE): if e.errno in (errno.EADDRINUSE, errno.WSAEADDRINUSE):
continue continue
@ -146,6 +141,7 @@ class CommandHandler(type):
# pylint: disable=protected-access # pylint: disable=protected-access
result = super(CommandHandler, mcs).__new__( result = super(CommandHandler, mcs).__new__(
mcs, name, bases, namespace) mcs, name, bases, namespace)
result.config = BMConfigParser()
result._handlers = {} result._handlers = {}
for func in namespace.values(): for func in namespace.values():
try: try:
@ -162,9 +158,15 @@ class command(object):
self.aliases = aliases self.aliases = aliases
def __call__(self, func): def __call__(self, func):
def wrapper(*args): if BMConfigParser().safeGet(
# return json.dumps(func(*args), indent=4) 'bitmessagesettings', 'apivariant') == 'legacy':
return func(*args) def wrapper(*args):
result = func(*args)
return result if isinstance(result, (int, str)) \
else json.dumps(result, indent=4)
wrapper.__doc__ = func.__doc__
else:
wrapper = func
# pylint: disable=protected-access # pylint: disable=protected-access
wrapper._cmd = self.aliases wrapper._cmd = self.aliases
return wrapper return wrapper
@ -175,7 +177,7 @@ class command(object):
# Modified by Jonathan Warren (Atheros). # Modified by Jonathan Warren (Atheros).
# Further modified by the Bitmessage developers # Further modified by the Bitmessage developers
# http://code.activestate.com/recipes/501148 # http://code.activestate.com/recipes/501148
class MySimpleXMLRPCRequestHandler(SimpleXMLRPCRequestHandler, object): class BMXMLRPCRequestHandler(SimpleXMLRPCRequestHandler, object):
"""The main API handler""" """The main API handler"""
__metaclass__ = CommandHandler __metaclass__ = CommandHandler
@ -253,9 +255,9 @@ class MySimpleXMLRPCRequestHandler(SimpleXMLRPCRequestHandler, object):
encstr = self.headers.get('Authorization').split()[1] encstr = self.headers.get('Authorization').split()[1]
emailid, password = encstr.decode('base64').split(':') emailid, password = encstr.decode('base64').split(':')
return ( return (
emailid == BMConfigParser().get( emailid == self.config.get(
'bitmessagesettings', 'apiusername') and 'bitmessagesettings', 'apiusername')
password == BMConfigParser().get( and password == self.config.get(
'bitmessagesettings', 'apipassword') 'bitmessagesettings', 'apipassword')
) )
else: else:
@ -274,7 +276,7 @@ class MySimpleXMLRPCRequestHandler(SimpleXMLRPCRequestHandler, object):
return base64.b64decode(text) return base64.b64decode(text)
except Exception as e: except Exception as e:
raise APIError( raise APIError(
22, "Decode error - %s. Had trouble while decoding string: %r" 22, 'Decode error - %s. Had trouble while decoding string: %r'
% (e, text) % (e, text)
) )
return None return None
@ -359,17 +361,17 @@ class MySimpleXMLRPCRequestHandler(SimpleXMLRPCRequestHandler, object):
@command('listAddresses', 'listAddresses2') @command('listAddresses', 'listAddresses2')
def HandleListAddresses(self): def HandleListAddresses(self):
data = [] data = []
for address in BMConfigParser().addresses(): for address in self.config.addresses():
streamNumber = decodeAddress(address)[2] streamNumber = decodeAddress(address)[2]
label = BMConfigParser().get(address, 'label') label = self.config.get(address, 'label')
if self._method == 'listAddresses2': if self._method == 'listAddresses2':
label = base64.b64encode(label) label = base64.b64encode(label)
data.append({ data.append({
'label': label, 'label': label,
'address': address, 'address': address,
'stream': streamNumber, 'stream': streamNumber,
'enabled': BMConfigParser().safeGetBoolean(address, 'enabled'), 'enabled': self.config.safeGetBoolean(address, 'enabled'),
'chan': BMConfigParser().safeGetBoolean(address, 'chan') 'chan': self.config.safeGetBoolean(address, 'chan')
}) })
return {'addresses': data} return {'addresses': data}
@ -426,12 +428,12 @@ class MySimpleXMLRPCRequestHandler(SimpleXMLRPCRequestHandler, object):
): ):
"""Handle a request to create a random address""" """Handle a request to create a random address"""
nonceTrialsPerByte = BMConfigParser().get( nonceTrialsPerByte = self.config.get(
'bitmessagesettings', 'defaultnoncetrialsperbyte' 'bitmessagesettings', 'defaultnoncetrialsperbyte'
) if not totalDifficulty else int( ) if not totalDifficulty else int(
defaults.networkDefaultProofOfWorkNonceTrialsPerByte * defaults.networkDefaultProofOfWorkNonceTrialsPerByte *
totalDifficulty) totalDifficulty)
payloadLengthExtraBytes = BMConfigParser().get( payloadLengthExtraBytes = self.config.get(
'bitmessagesettings', 'defaultpayloadlengthextrabytes' 'bitmessagesettings', 'defaultpayloadlengthextrabytes'
) if not smallMessageDifficulty else int( ) if not smallMessageDifficulty else int(
defaults.networkDefaultPayloadLengthExtraBytes * defaults.networkDefaultPayloadLengthExtraBytes *
@ -464,12 +466,12 @@ class MySimpleXMLRPCRequestHandler(SimpleXMLRPCRequestHandler, object):
"""Handle a request to create a deterministic address""" """Handle a request to create a deterministic address"""
# pylint: disable=too-many-branches, too-many-statements # pylint: disable=too-many-branches, too-many-statements
nonceTrialsPerByte = BMConfigParser().get( nonceTrialsPerByte = self.config.get(
'bitmessagesettings', 'defaultnoncetrialsperbyte' 'bitmessagesettings', 'defaultnoncetrialsperbyte'
) if not totalDifficulty else int( ) if not totalDifficulty else int(
defaults.networkDefaultProofOfWorkNonceTrialsPerByte * defaults.networkDefaultProofOfWorkNonceTrialsPerByte *
totalDifficulty) totalDifficulty)
payloadLengthExtraBytes = BMConfigParser().get( payloadLengthExtraBytes = self.config.get(
'bitmessagesettings', 'defaultpayloadlengthextrabytes' 'bitmessagesettings', 'defaultpayloadlengthextrabytes'
) if not smallMessageDifficulty else int( ) if not smallMessageDifficulty else int(
defaults.networkDefaultPayloadLengthExtraBytes * defaults.networkDefaultPayloadLengthExtraBytes *
@ -612,16 +614,16 @@ class MySimpleXMLRPCRequestHandler(SimpleXMLRPCRequestHandler, object):
"""Handle a request to leave a chan""" """Handle a request to leave a chan"""
self._verifyAddress(address) self._verifyAddress(address)
address = addBMIfNotPresent(address) address = addBMIfNotPresent(address)
if not BMConfigParser().safeGetBoolean(address, 'chan'): if not self.config.safeGetBoolean(address, 'chan'):
raise APIError( raise APIError(
25, 'Specified address is not a chan address.' 25, 'Specified address is not a chan address.'
' Use deleteAddress API call instead.') ' Use deleteAddress API call instead.')
try: try:
BMConfigParser().remove_section(address) self.config.remove_section(address)
except ConfigParser.NoSectionError: except ConfigParser.NoSectionError:
raise APIError( raise APIError(
13, 'Could not find this address in your keys.dat file.') 13, 'Could not find this address in your keys.dat file.')
BMConfigParser().save() self.config.save()
queues.UISignalQueue.put(('rerenderMessagelistFromLabels', '')) queues.UISignalQueue.put(('rerenderMessagelistFromLabels', ''))
queues.UISignalQueue.put(('rerenderMessagelistToLabels', '')) queues.UISignalQueue.put(('rerenderMessagelistToLabels', ''))
return "success" return "success"
@ -632,11 +634,11 @@ class MySimpleXMLRPCRequestHandler(SimpleXMLRPCRequestHandler, object):
self._verifyAddress(address) self._verifyAddress(address)
address = addBMIfNotPresent(address) address = addBMIfNotPresent(address)
try: try:
BMConfigParser().remove_section(address) self.config.remove_section(address)
except ConfigParser.NoSectionError: except ConfigParser.NoSectionError:
raise APIError( raise APIError(
13, 'Could not find this address in your keys.dat file.') 13, 'Could not find this address in your keys.dat file.')
BMConfigParser().save() self.config.save()
queues.UISignalQueue.put(('writeNewAddressToTable', ('', '', ''))) queues.UISignalQueue.put(('writeNewAddressToTable', ('', '', '')))
shared.reloadMyAddressHashes() shared.reloadMyAddressHashes()
return "success" return "success"
@ -829,7 +831,7 @@ class MySimpleXMLRPCRequestHandler(SimpleXMLRPCRequestHandler, object):
streamNumber, toRipe = self._verifyAddress(toAddress)[2:] streamNumber, toRipe = self._verifyAddress(toAddress)[2:]
self._verifyAddress(fromAddress) self._verifyAddress(fromAddress)
try: try:
fromAddressEnabled = BMConfigParser().getboolean( fromAddressEnabled = self.config.getboolean(
fromAddress, 'enabled') fromAddress, 'enabled')
except BaseException: except BaseException:
raise APIError( raise APIError(
@ -837,7 +839,7 @@ class MySimpleXMLRPCRequestHandler(SimpleXMLRPCRequestHandler, object):
if not fromAddressEnabled: if not fromAddressEnabled:
raise APIError(14, 'Your fromAddress is disabled. Cannot send.') raise APIError(14, 'Your fromAddress is disabled. Cannot send.')
stealthLevel = BMConfigParser().safeGetInt( stealthLevel = self.config.safeGetInt(
'bitmessagesettings', 'ackstealthlevel') 'bitmessagesettings', 'ackstealthlevel')
ackdata = genAckPayload(streamNumber, stealthLevel) ackdata = genAckPayload(streamNumber, stealthLevel)
@ -892,10 +894,10 @@ class MySimpleXMLRPCRequestHandler(SimpleXMLRPCRequestHandler, object):
fromAddress = addBMIfNotPresent(fromAddress) fromAddress = addBMIfNotPresent(fromAddress)
self._verifyAddress(fromAddress) self._verifyAddress(fromAddress)
try: try:
BMConfigParser().getboolean(fromAddress, 'enabled') self.config.getboolean(fromAddress, 'enabled')
except BaseException: except BaseException:
raise APIError( raise APIError(
13, 'could not find your fromAddress in the keys.dat file.') 13, 'Could not find your fromAddress in the keys.dat file.')
streamNumber = decodeAddress(fromAddress)[2] streamNumber = decodeAddress(fromAddress)[2]
ackdata = genAckPayload(streamNumber, 0) ackdata = genAckPayload(streamNumber, 0)
toAddress = str_broadcast_subscribers toAddress = str_broadcast_subscribers
@ -1186,20 +1188,20 @@ class MySimpleXMLRPCRequestHandler(SimpleXMLRPCRequestHandler, object):
except KeyError: except KeyError:
raise APIError(20, 'Invalid method: %s' % method) raise APIError(20, 'Invalid method: %s' % method)
except TypeError as e: except TypeError as e:
msg = "Unexpected internal error: %s" % e msg = 'Unexpected API Failure - %s' % e
if 'argument' not in str(e): if 'argument' not in str(e):
raise APIError(0, msg) raise APIError(0, msg)
argcount = len(params) argcount = len(params)
maxcount = func.func_code.co_argcount maxcount = func.func_code.co_argcount
if argcount > maxcount: if argcount > maxcount:
msg = ( msg = (
"Command %s takes at most %s parameters (%s given)" % 'Command %s takes at most %s parameters (%s given)' %
(method, maxcount, argcount)) (method, maxcount, argcount))
else: else:
mincount = maxcount - len(func.func_defaults or []) mincount = maxcount - len(func.func_defaults or [])
if argcount < mincount: if argcount < mincount:
msg = ( msg = (
"Command %s takes at least %s parameters (%s given)" % 'Command %s takes at least %s parameters (%s given)' %
(method, mincount, argcount)) (method, mincount, argcount))
raise APIError(0, msg) raise APIError(0, msg)
finally: finally:
@ -1212,16 +1214,27 @@ class MySimpleXMLRPCRequestHandler(SimpleXMLRPCRequestHandler, object):
validuser = self.APIAuthenticateClient() validuser = self.APIAuthenticateClient()
if not validuser: if not validuser:
time.sleep(2) time.sleep(2)
# ProtocolError?
return "RPC Username or password incorrect or HTTP header lacks authentication at all." return "RPC Username or password incorrect or HTTP header lacks authentication at all."
_fault = None
try: try:
return self._handle_request(method, params) return self._handle_request(method, params)
except APIError as e: except APIError as e:
return str(e) _fault = e
except varintDecodeError as e: except varintDecodeError as e:
logger.error(e) logger.error(e)
return "API Error 0026: Data contains a malformed varint. Some details: %s" % e _fault = APIError(
26, 'Data contains a malformed varint. Some details: %s' %
e)
except Exception as e: except Exception as e:
logger.exception(e) logger.exception(e)
_fault = APIError(21, 'Unexpected API Failure - %s' % e)
return "API Error 0021: Unexpected API Failure - %s" % e if _fault:
if self.config.safeGet(
'bitmessagesettings', 'apivariant') == 'legacy':
return str(_fault)
else:
raise _fault # pylint: disable=raising-bad-type

View File

@ -188,6 +188,8 @@ class Main(object):
'bitmessagesettings', 'apiusername', 'username') 'bitmessagesettings', 'apiusername', 'username')
config.set( config.set(
'bitmessagesettings', 'apipassword', 'password') 'bitmessagesettings', 'apipassword', 'password')
config.set(
'bitmessagesettings', 'apivariant', 'legacy')
config.set( config.set(
'bitmessagesettings', 'apinotifypath', 'bitmessagesettings', 'apinotifypath',
os.path.join(app_dir, 'tests', 'apinotify_handler.py') os.path.join(app_dir, 'tests', 'apinotify_handler.py')