Refactoring the API with metaclass and decorator
This commit is contained in:
parent
1571176082
commit
45b0659e4c
1055
src/api.py
1055
src/api.py
|
@ -1,12 +1,15 @@
|
||||||
|
# Copyright (c) 2012-2016 Jonathan Warren
|
||||||
|
# Copyright (c) 2012-2020 The Bitmessage developers
|
||||||
|
# pylint: disable=too-many-lines,no-self-use,unused-variable,unused-argument
|
||||||
|
|
||||||
"""
|
"""
|
||||||
This is not what you run to run the Bitmessage API. Instead, enable the API
|
This is not what you run to run the Bitmessage API. Instead, enable the API
|
||||||
( https://bitmessage.org/wiki/API ) and optionally enable daemon mode
|
( https://bitmessage.org/wiki/API ) and optionally enable daemon mode
|
||||||
( https://bitmessage.org/wiki/Daemon ) then run bitmessagemain.py.
|
( https://bitmessage.org/wiki/Daemon ) then run bitmessagemain.py.
|
||||||
"""
|
"""
|
||||||
# Copyright (c) 2012-2016 Jonathan Warren
|
|
||||||
# Copyright (c) 2012-2020 The Bitmessage developers
|
|
||||||
# pylint: disable=too-many-lines,no-self-use,unused-variable,unused-argument
|
|
||||||
import base64
|
import base64
|
||||||
|
import ConfigParser
|
||||||
import errno
|
import errno
|
||||||
import hashlib
|
import hashlib
|
||||||
import json
|
import json
|
||||||
|
@ -27,7 +30,6 @@ import queues
|
||||||
import shared
|
import shared
|
||||||
import shutdown
|
import shutdown
|
||||||
import state
|
import state
|
||||||
import threads
|
|
||||||
from addresses import (
|
from addresses import (
|
||||||
addBMIfNotPresent,
|
addBMIfNotPresent,
|
||||||
calculateInventoryHash,
|
calculateInventoryHash,
|
||||||
|
@ -44,6 +46,7 @@ from network.threads import StoppableThread
|
||||||
from version import softwareVersion
|
from version import softwareVersion
|
||||||
|
|
||||||
str_chan = '[chan]'
|
str_chan = '[chan]'
|
||||||
|
str_broadcast_subscribers = '[Broadcast subscribers]'
|
||||||
|
|
||||||
|
|
||||||
class APIError(Exception):
|
class APIError(Exception):
|
||||||
|
@ -134,15 +137,47 @@ class singleAPI(StoppableThread):
|
||||||
se.serve_forever()
|
se.serve_forever()
|
||||||
|
|
||||||
|
|
||||||
class MySimpleXMLRPCRequestHandler(SimpleXMLRPCRequestHandler):
|
class CommandHandler(type):
|
||||||
"""
|
"""
|
||||||
This is one of several classes that constitute the API
|
The metaclass for `BMRPCDispatcher` which fills _handlers dict by
|
||||||
|
methods decorated with @command
|
||||||
|
"""
|
||||||
|
def __new__(mcs, name, bases, namespace):
|
||||||
|
# pylint: disable=protected-access
|
||||||
|
result = super(CommandHandler, mcs).__new__(
|
||||||
|
mcs, name, bases, namespace)
|
||||||
|
result._handlers = {}
|
||||||
|
for func in namespace.values():
|
||||||
|
try:
|
||||||
|
for alias in getattr(func, '_cmd'):
|
||||||
|
result._handlers[alias] = func
|
||||||
|
except AttributeError:
|
||||||
|
pass
|
||||||
|
return result
|
||||||
|
|
||||||
This class was written by Vaibhav Bhatia.
|
|
||||||
Modified by Jonathan Warren (Atheros).
|
class command(object):
|
||||||
http://code.activestate.com/recipes/501148-xmlrpc-serverclient-which-does-cookie-handling-and/
|
"""Decorator for API command method"""
|
||||||
"""
|
def __init__(self, *aliases):
|
||||||
# pylint: disable=too-many-public-methods
|
self.aliases = aliases
|
||||||
|
|
||||||
|
def __call__(self, func):
|
||||||
|
def wrapper(*args):
|
||||||
|
# return json.dumps(func(*args), indent=4)
|
||||||
|
return func(*args)
|
||||||
|
# pylint: disable=protected-access
|
||||||
|
wrapper._cmd = self.aliases
|
||||||
|
return wrapper
|
||||||
|
|
||||||
|
|
||||||
|
# This is one of several classes that constitute the API
|
||||||
|
# This class was written by Vaibhav Bhatia.
|
||||||
|
# Modified by Jonathan Warren (Atheros).
|
||||||
|
# Further modified by the Bitmessage developers
|
||||||
|
# http://code.activestate.com/recipes/501148
|
||||||
|
class MySimpleXMLRPCRequestHandler(SimpleXMLRPCRequestHandler, object):
|
||||||
|
"""The main API handler"""
|
||||||
|
__metaclass__ = CommandHandler
|
||||||
|
|
||||||
def do_POST(self):
|
def do_POST(self):
|
||||||
"""
|
"""
|
||||||
|
@ -215,7 +250,7 @@ class MySimpleXMLRPCRequestHandler(SimpleXMLRPCRequestHandler):
|
||||||
|
|
||||||
if 'Authorization' in self.headers:
|
if 'Authorization' in self.headers:
|
||||||
# handle Basic authentication
|
# handle Basic authentication
|
||||||
_, encstr = self.headers.get('Authorization').split()
|
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 == BMConfigParser().get(
|
||||||
|
@ -281,66 +316,46 @@ class MySimpleXMLRPCRequestHandler(SimpleXMLRPCRequestHandler):
|
||||||
|
|
||||||
# Request Handlers
|
# Request Handlers
|
||||||
|
|
||||||
def HandleListAddresses(self, method):
|
@command('listAddresses', 'listAddresses2')
|
||||||
"""Handle a request to list addresses"""
|
def HandleListAddresses(self):
|
||||||
data = '{"addresses":['
|
data = []
|
||||||
for addressInKeysFile in BMConfigParser().addresses():
|
for address in BMConfigParser().addresses():
|
||||||
status, addressVersionNumber, streamNumber, hash01 = decodeAddress(
|
streamNumber = decodeAddress(address)[2]
|
||||||
addressInKeysFile)
|
label = BMConfigParser().get(address, 'label')
|
||||||
if len(data) > 20:
|
if self._method == 'listAddresses2':
|
||||||
data += ','
|
|
||||||
if BMConfigParser().has_option(addressInKeysFile, 'chan'):
|
|
||||||
chan = BMConfigParser().getboolean(addressInKeysFile, 'chan')
|
|
||||||
else:
|
|
||||||
chan = False
|
|
||||||
label = BMConfigParser().get(addressInKeysFile, 'label')
|
|
||||||
if method == 'listAddresses2':
|
|
||||||
label = base64.b64encode(label)
|
label = base64.b64encode(label)
|
||||||
data += json.dumps({
|
data.append({
|
||||||
'label': label,
|
'label': label,
|
||||||
'address': addressInKeysFile,
|
'address': address,
|
||||||
'stream': streamNumber,
|
'stream': streamNumber,
|
||||||
'enabled':
|
'enabled': BMConfigParser().safeGetBoolean(address, 'enabled'),
|
||||||
BMConfigParser().getboolean(addressInKeysFile, 'enabled'),
|
'chan': BMConfigParser().safeGetBoolean(address, 'chan')
|
||||||
'chan': chan
|
})
|
||||||
}, indent=4, separators=(',', ': '))
|
return {'addresses': data}
|
||||||
data += ']}'
|
|
||||||
return data
|
|
||||||
|
|
||||||
def HandleListAddressBookEntries(self, params):
|
# the listAddressbook alias should be removed eventually.
|
||||||
"""Handle a request to list address book entries"""
|
@command('listAddressBookEntries', 'listAddressbook')
|
||||||
|
def HandleListAddressBookEntries(self, label=None):
|
||||||
if len(params) == 1:
|
queryreturn = sqlQuery(
|
||||||
label, = params
|
"SELECT label, address from addressbook WHERE label = ?",
|
||||||
label = self._decode(label, "base64")
|
label
|
||||||
queryreturn = sqlQuery(
|
) if label else sqlQuery("SELECT label, address from addressbook")
|
||||||
"SELECT label, address from addressbook WHERE label = ?",
|
data = []
|
||||||
label)
|
for label, address in queryreturn:
|
||||||
elif len(params) > 1:
|
|
||||||
raise APIError(0, "Too many paremeters, max 1")
|
|
||||||
else:
|
|
||||||
queryreturn = sqlQuery("SELECT label, address from addressbook")
|
|
||||||
data = '{"addresses":['
|
|
||||||
for row in queryreturn:
|
|
||||||
label, address = row
|
|
||||||
label = shared.fixPotentiallyInvalidUTF8Data(label)
|
label = shared.fixPotentiallyInvalidUTF8Data(label)
|
||||||
if len(data) > 20:
|
data.append({
|
||||||
data += ','
|
|
||||||
data += json.dumps({
|
|
||||||
'label': base64.b64encode(label),
|
'label': base64.b64encode(label),
|
||||||
'address': address}, indent=4, separators=(',', ': '))
|
'address': address
|
||||||
data += ']}'
|
})
|
||||||
return data
|
return {'addresses': data}
|
||||||
|
|
||||||
def HandleAddAddressBookEntry(self, params):
|
# the addAddressbook alias should be deleted eventually.
|
||||||
"""Handle a request to add an address book entry"""
|
@command('addAddressBookEntry', 'addAddressbook')
|
||||||
|
def HandleAddAddressBookEntry(self, address, label):
|
||||||
if len(params) != 2:
|
|
||||||
raise APIError(0, "I need label and address")
|
|
||||||
address, label = params
|
|
||||||
label = self._decode(label, "base64")
|
label = self._decode(label, "base64")
|
||||||
address = addBMIfNotPresent(address)
|
address = addBMIfNotPresent(address)
|
||||||
self._verifyAddress(address)
|
self._verifyAddress(address)
|
||||||
|
# TODO: add unique together constraint in the table
|
||||||
queryreturn = sqlQuery(
|
queryreturn = sqlQuery(
|
||||||
"SELECT address FROM addressbook WHERE address=?", address)
|
"SELECT address FROM addressbook WHERE address=?", address)
|
||||||
if queryreturn != []:
|
if queryreturn != []:
|
||||||
|
@ -353,12 +368,9 @@ class MySimpleXMLRPCRequestHandler(SimpleXMLRPCRequestHandler):
|
||||||
queues.UISignalQueue.put(('rerenderAddressBook', ''))
|
queues.UISignalQueue.put(('rerenderAddressBook', ''))
|
||||||
return "Added address %s to address book" % address
|
return "Added address %s to address book" % address
|
||||||
|
|
||||||
def HandleDeleteAddressBookEntry(self, params):
|
# the deleteAddressbook alias should be deleted eventually.
|
||||||
"""Handle a request to delete an address book entry"""
|
@command('deleteAddressBookEntry', 'deleteAddressbook')
|
||||||
|
def HandleDeleteAddressBookEntry(self, address):
|
||||||
if len(params) != 1:
|
|
||||||
raise APIError(0, "I need an address")
|
|
||||||
address, = params
|
|
||||||
address = addBMIfNotPresent(address)
|
address = addBMIfNotPresent(address)
|
||||||
self._verifyAddress(address)
|
self._verifyAddress(address)
|
||||||
sqlExecute('DELETE FROM addressbook WHERE address=?', address)
|
sqlExecute('DELETE FROM addressbook WHERE address=?', address)
|
||||||
|
@ -367,49 +379,35 @@ class MySimpleXMLRPCRequestHandler(SimpleXMLRPCRequestHandler):
|
||||||
queues.UISignalQueue.put(('rerenderAddressBook', ''))
|
queues.UISignalQueue.put(('rerenderAddressBook', ''))
|
||||||
return "Deleted address book entry for %s if it existed" % address
|
return "Deleted address book entry for %s if it existed" % address
|
||||||
|
|
||||||
def HandleCreateRandomAddress(self, params):
|
@command('createRandomAddress')
|
||||||
|
def HandleCreateRandomAddress(
|
||||||
|
self, label, eighteenByteRipe=False, totalDifficulty=0,
|
||||||
|
smallMessageDifficulty=0
|
||||||
|
):
|
||||||
"""Handle a request to create a random address"""
|
"""Handle a request to create a random address"""
|
||||||
|
|
||||||
if not params:
|
nonceTrialsPerByte = BMConfigParser().get(
|
||||||
raise APIError(0, 'I need parameters!')
|
'bitmessagesettings', 'defaultnoncetrialsperbyte'
|
||||||
|
) if not totalDifficulty else int(
|
||||||
|
defaults.networkDefaultProofOfWorkNonceTrialsPerByte *
|
||||||
|
totalDifficulty)
|
||||||
|
payloadLengthExtraBytes = BMConfigParser().get(
|
||||||
|
'bitmessagesettings', 'defaultpayloadlengthextrabytes'
|
||||||
|
) if not smallMessageDifficulty else int(
|
||||||
|
defaults.networkDefaultPayloadLengthExtraBytes *
|
||||||
|
smallMessageDifficulty)
|
||||||
|
|
||||||
elif len(params) == 1:
|
if not isinstance(eighteenByteRipe, bool):
|
||||||
label, = params
|
raise APIError(
|
||||||
eighteenByteRipe = False
|
23, 'Bool expected in eighteenByteRipe, saw %s instead' %
|
||||||
nonceTrialsPerByte = BMConfigParser().get(
|
type(eighteenByteRipe))
|
||||||
'bitmessagesettings', 'defaultnoncetrialsperbyte')
|
|
||||||
payloadLengthExtraBytes = BMConfigParser().get(
|
|
||||||
'bitmessagesettings', 'defaultpayloadlengthextrabytes')
|
|
||||||
elif len(params) == 2:
|
|
||||||
label, eighteenByteRipe = params
|
|
||||||
nonceTrialsPerByte = BMConfigParser().get(
|
|
||||||
'bitmessagesettings', 'defaultnoncetrialsperbyte')
|
|
||||||
payloadLengthExtraBytes = BMConfigParser().get(
|
|
||||||
'bitmessagesettings', 'defaultpayloadlengthextrabytes')
|
|
||||||
elif len(params) == 3:
|
|
||||||
label, eighteenByteRipe, totalDifficulty = params
|
|
||||||
nonceTrialsPerByte = int(
|
|
||||||
defaults.networkDefaultProofOfWorkNonceTrialsPerByte *
|
|
||||||
totalDifficulty)
|
|
||||||
payloadLengthExtraBytes = BMConfigParser().get(
|
|
||||||
'bitmessagesettings', 'defaultpayloadlengthextrabytes')
|
|
||||||
elif len(params) == 4:
|
|
||||||
label, eighteenByteRipe, totalDifficulty, \
|
|
||||||
smallMessageDifficulty = params
|
|
||||||
nonceTrialsPerByte = int(
|
|
||||||
defaults.networkDefaultProofOfWorkNonceTrialsPerByte *
|
|
||||||
totalDifficulty)
|
|
||||||
payloadLengthExtraBytes = int(
|
|
||||||
defaults.networkDefaultPayloadLengthExtraBytes *
|
|
||||||
smallMessageDifficulty)
|
|
||||||
else:
|
|
||||||
raise APIError(0, 'Too many parameters!')
|
|
||||||
label = self._decode(label, "base64")
|
label = self._decode(label, "base64")
|
||||||
try:
|
try:
|
||||||
unicode(label, 'utf-8')
|
unicode(label, 'utf-8')
|
||||||
except BaseException:
|
except UnicodeDecodeError:
|
||||||
raise APIError(17, 'Label is not valid UTF-8 data.')
|
raise APIError(17, 'Label is not valid UTF-8 data.')
|
||||||
queues.apiAddressGeneratorReturnQueue.queue.clear()
|
queues.apiAddressGeneratorReturnQueue.queue.clear()
|
||||||
|
# FIXME hard coded stream no
|
||||||
streamNumberForAddress = 1
|
streamNumberForAddress = 1
|
||||||
queues.addressGeneratorQueue.put((
|
queues.addressGeneratorQueue.put((
|
||||||
'createRandomAddress', 4, streamNumberForAddress, label, 1, "",
|
'createRandomAddress', 4, streamNumberForAddress, label, 1, "",
|
||||||
|
@ -417,81 +415,26 @@ class MySimpleXMLRPCRequestHandler(SimpleXMLRPCRequestHandler):
|
||||||
))
|
))
|
||||||
return queues.apiAddressGeneratorReturnQueue.get()
|
return queues.apiAddressGeneratorReturnQueue.get()
|
||||||
|
|
||||||
def HandleCreateDeterministicAddresses(self, params):
|
@command('createDeterministicAddresses')
|
||||||
|
def HandleCreateDeterministicAddresses(
|
||||||
|
self, passphrase, numberOfAddresses=1, addressVersionNumber=0,
|
||||||
|
streamNumber=0, eighteenByteRipe=False, totalDifficulty=0,
|
||||||
|
smallMessageDifficulty=0
|
||||||
|
):
|
||||||
"""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
|
||||||
|
|
||||||
if not params:
|
nonceTrialsPerByte = BMConfigParser().get(
|
||||||
raise APIError(0, 'I need parameters!')
|
'bitmessagesettings', 'defaultnoncetrialsperbyte'
|
||||||
|
) if not totalDifficulty else int(
|
||||||
|
defaults.networkDefaultProofOfWorkNonceTrialsPerByte *
|
||||||
|
totalDifficulty)
|
||||||
|
payloadLengthExtraBytes = BMConfigParser().get(
|
||||||
|
'bitmessagesettings', 'defaultpayloadlengthextrabytes'
|
||||||
|
) if not smallMessageDifficulty else int(
|
||||||
|
defaults.networkDefaultPayloadLengthExtraBytes *
|
||||||
|
smallMessageDifficulty)
|
||||||
|
|
||||||
elif len(params) == 1:
|
|
||||||
passphrase, = params
|
|
||||||
numberOfAddresses = 1
|
|
||||||
addressVersionNumber = 0
|
|
||||||
streamNumber = 0
|
|
||||||
eighteenByteRipe = False
|
|
||||||
nonceTrialsPerByte = BMConfigParser().get(
|
|
||||||
'bitmessagesettings', 'defaultnoncetrialsperbyte')
|
|
||||||
payloadLengthExtraBytes = BMConfigParser().get(
|
|
||||||
'bitmessagesettings', 'defaultpayloadlengthextrabytes')
|
|
||||||
|
|
||||||
elif len(params) == 2:
|
|
||||||
passphrase, numberOfAddresses = params
|
|
||||||
addressVersionNumber = 0
|
|
||||||
streamNumber = 0
|
|
||||||
eighteenByteRipe = False
|
|
||||||
nonceTrialsPerByte = BMConfigParser().get(
|
|
||||||
'bitmessagesettings', 'defaultnoncetrialsperbyte')
|
|
||||||
payloadLengthExtraBytes = BMConfigParser().get(
|
|
||||||
'bitmessagesettings', 'defaultpayloadlengthextrabytes')
|
|
||||||
|
|
||||||
elif len(params) == 3:
|
|
||||||
passphrase, numberOfAddresses, addressVersionNumber = params
|
|
||||||
streamNumber = 0
|
|
||||||
eighteenByteRipe = False
|
|
||||||
nonceTrialsPerByte = BMConfigParser().get(
|
|
||||||
'bitmessagesettings', 'defaultnoncetrialsperbyte')
|
|
||||||
payloadLengthExtraBytes = BMConfigParser().get(
|
|
||||||
'bitmessagesettings', 'defaultpayloadlengthextrabytes')
|
|
||||||
|
|
||||||
elif len(params) == 4:
|
|
||||||
passphrase, numberOfAddresses, addressVersionNumber, \
|
|
||||||
streamNumber = params
|
|
||||||
eighteenByteRipe = False
|
|
||||||
nonceTrialsPerByte = BMConfigParser().get(
|
|
||||||
'bitmessagesettings', 'defaultnoncetrialsperbyte')
|
|
||||||
payloadLengthExtraBytes = BMConfigParser().get(
|
|
||||||
'bitmessagesettings', 'defaultpayloadlengthextrabytes')
|
|
||||||
|
|
||||||
elif len(params) == 5:
|
|
||||||
passphrase, numberOfAddresses, addressVersionNumber, \
|
|
||||||
streamNumber, eighteenByteRipe = params
|
|
||||||
nonceTrialsPerByte = BMConfigParser().get(
|
|
||||||
'bitmessagesettings', 'defaultnoncetrialsperbyte')
|
|
||||||
payloadLengthExtraBytes = BMConfigParser().get(
|
|
||||||
'bitmessagesettings', 'defaultpayloadlengthextrabytes')
|
|
||||||
|
|
||||||
elif len(params) == 6:
|
|
||||||
passphrase, numberOfAddresses, addressVersionNumber, \
|
|
||||||
streamNumber, eighteenByteRipe, totalDifficulty = params
|
|
||||||
nonceTrialsPerByte = int(
|
|
||||||
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)
|
|
||||||
payloadLengthExtraBytes = int(
|
|
||||||
defaults.networkDefaultPayloadLengthExtraBytes *
|
|
||||||
smallMessageDifficulty)
|
|
||||||
else:
|
|
||||||
raise APIError(0, 'Too many parameters!')
|
|
||||||
if not passphrase:
|
if not passphrase:
|
||||||
raise APIError(1, 'The specified passphrase is blank.')
|
raise APIError(1, 'The specified passphrase is blank.')
|
||||||
if not isinstance(eighteenByteRipe, bool):
|
if not isinstance(eighteenByteRipe, bool):
|
||||||
|
@ -502,13 +445,13 @@ class MySimpleXMLRPCRequestHandler(SimpleXMLRPCRequestHandler):
|
||||||
# 0 means "just use the proper addressVersionNumber"
|
# 0 means "just use the proper addressVersionNumber"
|
||||||
if addressVersionNumber == 0:
|
if addressVersionNumber == 0:
|
||||||
addressVersionNumber = 4
|
addressVersionNumber = 4
|
||||||
if addressVersionNumber != 3 and addressVersionNumber != 4:
|
if addressVersionNumber not in (3, 4):
|
||||||
raise APIError(
|
raise APIError(
|
||||||
2, 'The address version number currently must be 3, 4, or 0'
|
2, 'The address version number currently must be 3, 4, or 0'
|
||||||
' (which means auto-select). %i isn\'t supported.' %
|
' (which means auto-select). %i isn\'t supported.' %
|
||||||
addressVersionNumber)
|
addressVersionNumber)
|
||||||
if streamNumber == 0: # 0 means "just use the most available stream"
|
if streamNumber == 0: # 0 means "just use the most available stream"
|
||||||
streamNumber = 1
|
streamNumber = 1 # FIXME hard coded stream no
|
||||||
if streamNumber != 1:
|
if streamNumber != 1:
|
||||||
raise APIError(
|
raise APIError(
|
||||||
3, 'The stream number must be 1 (or 0 which means'
|
3, 'The stream number must be 1 (or 0 which means'
|
||||||
|
@ -533,27 +476,20 @@ class MySimpleXMLRPCRequestHandler(SimpleXMLRPCRequestHandler):
|
||||||
'unused API address', numberOfAddresses, passphrase,
|
'unused API address', numberOfAddresses, passphrase,
|
||||||
eighteenByteRipe, nonceTrialsPerByte, payloadLengthExtraBytes
|
eighteenByteRipe, nonceTrialsPerByte, payloadLengthExtraBytes
|
||||||
))
|
))
|
||||||
data = '{"addresses":['
|
|
||||||
queueReturn = queues.apiAddressGeneratorReturnQueue.get()
|
|
||||||
for item in queueReturn:
|
|
||||||
if len(data) > 20:
|
|
||||||
data += ','
|
|
||||||
data += "\"" + item + "\""
|
|
||||||
data += ']}'
|
|
||||||
return data
|
|
||||||
|
|
||||||
def HandleGetDeterministicAddress(self, params):
|
return {'addresses': queues.apiAddressGeneratorReturnQueue.get()}
|
||||||
|
|
||||||
|
@command('getDeterministicAddress')
|
||||||
|
def HandleGetDeterministicAddress(
|
||||||
|
self, passphrase, addressVersionNumber, streamNumber):
|
||||||
"""Handle a request to get a deterministic address"""
|
"""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
|
numberOfAddresses = 1
|
||||||
eighteenByteRipe = False
|
eighteenByteRipe = False
|
||||||
if not passphrase:
|
if not passphrase:
|
||||||
raise APIError(1, 'The specified passphrase is blank.')
|
raise APIError(1, 'The specified passphrase is blank.')
|
||||||
passphrase = self._decode(passphrase, "base64")
|
passphrase = self._decode(passphrase, "base64")
|
||||||
if addressVersionNumber != 3 and addressVersionNumber != 4:
|
if addressVersionNumber not in (3, 4):
|
||||||
raise APIError(
|
raise APIError(
|
||||||
2, 'The address version number currently must be 3 or 4. %i'
|
2, 'The address version number currently must be 3 or 4. %i'
|
||||||
' isn\'t supported.' % addressVersionNumber)
|
' isn\'t supported.' % addressVersionNumber)
|
||||||
|
@ -571,16 +507,11 @@ class MySimpleXMLRPCRequestHandler(SimpleXMLRPCRequestHandler):
|
||||||
))
|
))
|
||||||
return queues.apiAddressGeneratorReturnQueue.get()
|
return queues.apiAddressGeneratorReturnQueue.get()
|
||||||
|
|
||||||
def HandleCreateChan(self, params):
|
@command('createChan')
|
||||||
|
def HandleCreateChan(self, passphrase):
|
||||||
"""Handle a request to create a chan"""
|
"""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")
|
passphrase = self._decode(passphrase, "base64")
|
||||||
|
|
||||||
if not passphrase:
|
if not passphrase:
|
||||||
raise APIError(1, 'The specified passphrase is blank.')
|
raise APIError(1, 'The specified passphrase is blank.')
|
||||||
# It would be nice to make the label the passphrase but it is
|
# It would be nice to make the label the passphrase but it is
|
||||||
|
@ -588,7 +519,7 @@ class MySimpleXMLRPCRequestHandler(SimpleXMLRPCRequestHandler):
|
||||||
try:
|
try:
|
||||||
unicode(passphrase, 'utf-8')
|
unicode(passphrase, 'utf-8')
|
||||||
label = str_chan + ' ' + passphrase
|
label = str_chan + ' ' + passphrase
|
||||||
except BaseException:
|
except UnicodeDecodeError:
|
||||||
label = str_chan + ' ' + repr(passphrase)
|
label = str_chan + ' ' + repr(passphrase)
|
||||||
|
|
||||||
addressVersionNumber = 4
|
addressVersionNumber = 4
|
||||||
|
@ -601,18 +532,15 @@ class MySimpleXMLRPCRequestHandler(SimpleXMLRPCRequestHandler):
|
||||||
passphrase, True
|
passphrase, True
|
||||||
))
|
))
|
||||||
queueReturn = queues.apiAddressGeneratorReturnQueue.get()
|
queueReturn = queues.apiAddressGeneratorReturnQueue.get()
|
||||||
if not queueReturn:
|
try:
|
||||||
|
return queueReturn[0]
|
||||||
|
except IndexError:
|
||||||
raise APIError(24, 'Chan address is already present.')
|
raise APIError(24, 'Chan address is already present.')
|
||||||
address = queueReturn[0]
|
|
||||||
return address
|
|
||||||
|
|
||||||
def HandleJoinChan(self, params):
|
@command('joinChan')
|
||||||
|
def HandleJoinChan(self, passphrase, suppliedAddress):
|
||||||
"""Handle a request to join a chan"""
|
"""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")
|
passphrase = self._decode(passphrase, "base64")
|
||||||
if not passphrase:
|
if not passphrase:
|
||||||
raise APIError(1, 'The specified passphrase is blank.')
|
raise APIError(1, 'The specified passphrase is blank.')
|
||||||
|
@ -621,84 +549,73 @@ class MySimpleXMLRPCRequestHandler(SimpleXMLRPCRequestHandler):
|
||||||
try:
|
try:
|
||||||
unicode(passphrase, 'utf-8')
|
unicode(passphrase, 'utf-8')
|
||||||
label = str_chan + ' ' + passphrase
|
label = str_chan + ' ' + passphrase
|
||||||
except BaseException:
|
except UnicodeDecodeError:
|
||||||
label = str_chan + ' ' + repr(passphrase)
|
label = str_chan + ' ' + repr(passphrase)
|
||||||
status, addressVersionNumber, streamNumber, toRipe = (
|
|
||||||
self._verifyAddress(suppliedAddress))
|
self._verifyAddress(suppliedAddress)
|
||||||
suppliedAddress = addBMIfNotPresent(suppliedAddress)
|
suppliedAddress = addBMIfNotPresent(suppliedAddress)
|
||||||
queues.apiAddressGeneratorReturnQueue.queue.clear()
|
queues.apiAddressGeneratorReturnQueue.queue.clear()
|
||||||
queues.addressGeneratorQueue.put((
|
queues.addressGeneratorQueue.put((
|
||||||
'joinChan', suppliedAddress, label, passphrase, True
|
'joinChan', suppliedAddress, label, passphrase, True
|
||||||
))
|
))
|
||||||
addressGeneratorReturnValue = \
|
queueReturn = queues.apiAddressGeneratorReturnQueue.get()
|
||||||
queues.apiAddressGeneratorReturnQueue.get()
|
try:
|
||||||
|
if queueReturn[0] == 'chan name does not match address':
|
||||||
if addressGeneratorReturnValue[0] == \
|
raise APIError(18, 'Chan name does not match address.')
|
||||||
'chan name does not match address':
|
except IndexError:
|
||||||
raise APIError(18, 'Chan name does not match address.')
|
|
||||||
if not addressGeneratorReturnValue:
|
|
||||||
raise APIError(24, 'Chan address is already present.')
|
raise APIError(24, 'Chan address is already present.')
|
||||||
|
|
||||||
return "success"
|
return "success"
|
||||||
|
|
||||||
def HandleLeaveChan(self, params):
|
@command('leaveChan')
|
||||||
|
def HandleLeaveChan(self, address):
|
||||||
"""Handle a request to leave a chan"""
|
"""Handle a request to leave a chan"""
|
||||||
|
self._verifyAddress(address)
|
||||||
if not params:
|
|
||||||
raise APIError(0, 'I need parameters.')
|
|
||||||
elif len(params) == 1:
|
|
||||||
address, = params
|
|
||||||
status, addressVersionNumber, streamNumber, toRipe = (
|
|
||||||
self._verifyAddress(address))
|
|
||||||
address = addBMIfNotPresent(address)
|
address = addBMIfNotPresent(address)
|
||||||
if not BMConfigParser().has_section(address):
|
|
||||||
raise APIError(
|
|
||||||
13, 'Could not find this address in your keys.dat file.')
|
|
||||||
if not BMConfigParser().safeGetBoolean(address, 'chan'):
|
if not BMConfigParser().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.')
|
||||||
BMConfigParser().remove_section(address)
|
try:
|
||||||
with open(state.appdata + 'keys.dat', 'wb') as configfile:
|
BMConfigParser().remove_section(address)
|
||||||
BMConfigParser().write(configfile)
|
except ConfigParser.NoSectionError:
|
||||||
return 'success'
|
|
||||||
|
|
||||||
def HandleDeleteAddress(self, params):
|
|
||||||
"""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))
|
|
||||||
address = addBMIfNotPresent(address)
|
|
||||||
if not BMConfigParser().has_section(address):
|
|
||||||
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().remove_section(address)
|
BMConfigParser().save()
|
||||||
with open(state.appdata + 'keys.dat', 'wb') as configfile:
|
queues.UISignalQueue.put(('rerenderMessagelistFromLabels', ''))
|
||||||
BMConfigParser().write(configfile)
|
queues.UISignalQueue.put(('rerenderMessagelistToLabels', ''))
|
||||||
|
return "success"
|
||||||
|
|
||||||
|
@command('deleteAddress')
|
||||||
|
def HandleDeleteAddress(self, address):
|
||||||
|
"""Handle a request to delete an address"""
|
||||||
|
self._verifyAddress(address)
|
||||||
|
address = addBMIfNotPresent(address)
|
||||||
|
try:
|
||||||
|
BMConfigParser().remove_section(address)
|
||||||
|
except ConfigParser.NoSectionError:
|
||||||
|
raise APIError(
|
||||||
|
13, 'Could not find this address in your keys.dat file.')
|
||||||
|
BMConfigParser().save()
|
||||||
queues.UISignalQueue.put(('writeNewAddressToTable', ('', '', '')))
|
queues.UISignalQueue.put(('writeNewAddressToTable', ('', '', '')))
|
||||||
shared.reloadMyAddressHashes()
|
shared.reloadMyAddressHashes()
|
||||||
return 'success'
|
return "success"
|
||||||
|
|
||||||
def HandleGetAllInboxMessages(self, params):
|
@command('getAllInboxMessages')
|
||||||
|
def HandleGetAllInboxMessages(self):
|
||||||
"""Handle a request to get all inbox messages"""
|
"""Handle a request to get all inbox messages"""
|
||||||
|
|
||||||
queryreturn = sqlQuery(
|
queryreturn = sqlQuery(
|
||||||
"SELECT msgid, toaddress, fromaddress, subject, received, message,"
|
"SELECT msgid, toaddress, fromaddress, subject, received, message,"
|
||||||
" encodingtype, read FROM inbox where folder='inbox'"
|
" encodingtype, read FROM inbox WHERE folder='inbox'"
|
||||||
" ORDER BY received"
|
" ORDER BY received"
|
||||||
)
|
)
|
||||||
data = '{"inboxMessages":['
|
data = []
|
||||||
for row in queryreturn:
|
for (msgid, toAddress, fromAddress, subject, received, message,
|
||||||
msgid, toAddress, fromAddress, subject, received, message, \
|
encodingtype, read) in queryreturn:
|
||||||
encodingtype, read = row
|
|
||||||
subject = shared.fixPotentiallyInvalidUTF8Data(subject)
|
subject = shared.fixPotentiallyInvalidUTF8Data(subject)
|
||||||
message = shared.fixPotentiallyInvalidUTF8Data(message)
|
message = shared.fixPotentiallyInvalidUTF8Data(message)
|
||||||
if len(data) > 25:
|
data.append({
|
||||||
data += ','
|
|
||||||
data += json.dumps({
|
|
||||||
'msgid': hexlify(msgid),
|
'msgid': hexlify(msgid),
|
||||||
'toAddress': toAddress,
|
'toAddress': toAddress,
|
||||||
'fromAddress': fromAddress,
|
'fromAddress': fromAddress,
|
||||||
|
@ -706,35 +623,27 @@ class MySimpleXMLRPCRequestHandler(SimpleXMLRPCRequestHandler):
|
||||||
'message': base64.b64encode(message),
|
'message': base64.b64encode(message),
|
||||||
'encodingType': encodingtype,
|
'encodingType': encodingtype,
|
||||||
'receivedTime': received,
|
'receivedTime': received,
|
||||||
'read': read}, indent=4, separators=(',', ': '))
|
'read': read
|
||||||
data += ']}'
|
})
|
||||||
return data
|
return {"inboxMessages": data}
|
||||||
|
|
||||||
def HandleGetAllInboxMessageIds(self, params):
|
@command('getAllInboxMessageIds', 'getAllInboxMessageIDs')
|
||||||
|
def HandleGetAllInboxMessageIds(self):
|
||||||
"""Handle a request to get all inbox message IDs"""
|
"""Handle a request to get all inbox message IDs"""
|
||||||
|
|
||||||
queryreturn = sqlQuery(
|
queryreturn = sqlQuery(
|
||||||
"SELECT msgid FROM inbox where folder='inbox' ORDER BY received")
|
"SELECT msgid FROM inbox where folder='inbox' ORDER BY received")
|
||||||
data = '{"inboxMessageIds":['
|
|
||||||
for row in queryreturn:
|
|
||||||
msgid = row[0]
|
|
||||||
if len(data) > 25:
|
|
||||||
data += ','
|
|
||||||
data += json.dumps(
|
|
||||||
{'msgid': hexlify(msgid)}, indent=4, separators=(',', ': '))
|
|
||||||
data += ']}'
|
|
||||||
return data
|
|
||||||
|
|
||||||
def HandleGetInboxMessageById(self, params):
|
return {"inboxMessageIds": [
|
||||||
|
{'msgid': hexlify(msgid)} for msgid, in queryreturn
|
||||||
|
]}
|
||||||
|
|
||||||
|
@command('getInboxMessageById', 'getInboxMessageByID')
|
||||||
|
def HandleGetInboxMessageById(self, hid, readStatus=None):
|
||||||
"""Handle a request to get an inbox messsage by ID"""
|
"""Handle a request to get an inbox messsage by ID"""
|
||||||
|
|
||||||
if not params:
|
msgid = self._decode(hid, "hex")
|
||||||
raise APIError(0, 'I need parameters!')
|
if readStatus is not None:
|
||||||
elif len(params) == 1:
|
|
||||||
msgid = self._decode(params[0], "hex")
|
|
||||||
elif len(params) >= 2:
|
|
||||||
msgid = self._decode(params[0], "hex")
|
|
||||||
readStatus = params[1]
|
|
||||||
if not isinstance(readStatus, bool):
|
if not isinstance(readStatus, bool):
|
||||||
raise APIError(
|
raise APIError(
|
||||||
23, 'Bool expected in readStatus, saw %s instead.' %
|
23, 'Bool expected in readStatus, saw %s instead.' %
|
||||||
|
@ -742,34 +651,39 @@ class MySimpleXMLRPCRequestHandler(SimpleXMLRPCRequestHandler):
|
||||||
queryreturn = sqlQuery(
|
queryreturn = sqlQuery(
|
||||||
"SELECT read FROM inbox WHERE msgid=?", msgid)
|
"SELECT read FROM inbox WHERE msgid=?", msgid)
|
||||||
# UPDATE is slow, only update if status is different
|
# UPDATE is slow, only update if status is different
|
||||||
if queryreturn != [] and (queryreturn[0][0] == 1) != readStatus:
|
try:
|
||||||
sqlExecute(
|
if (queryreturn[0][0] == 1) != readStatus:
|
||||||
"UPDATE inbox set read = ? WHERE msgid=?",
|
sqlExecute(
|
||||||
readStatus, msgid)
|
"UPDATE inbox set read = ? WHERE msgid=?",
|
||||||
queues.UISignalQueue.put(('changedInboxUnread', None))
|
readStatus, msgid)
|
||||||
|
queues.UISignalQueue.put(('changedInboxUnread', None))
|
||||||
|
except IndexError:
|
||||||
|
pass
|
||||||
queryreturn = sqlQuery(
|
queryreturn = sqlQuery(
|
||||||
"SELECT msgid, toaddress, fromaddress, subject, received, message,"
|
"SELECT toaddress, fromaddress, subject, received, message,"
|
||||||
" encodingtype, read FROM inbox WHERE msgid=?", msgid
|
" encodingtype, read FROM inbox WHERE msgid=?", msgid
|
||||||
)
|
)
|
||||||
data = '{"inboxMessage":['
|
data = []
|
||||||
for row in queryreturn:
|
try:
|
||||||
msgid, toAddress, fromAddress, subject, received, message, \
|
(toAddress, fromAddress, subject, received, message, encodingtype,
|
||||||
encodingtype, read = row
|
read) = queryreturn[0]
|
||||||
subject = shared.fixPotentiallyInvalidUTF8Data(subject)
|
except IndexError:
|
||||||
message = shared.fixPotentiallyInvalidUTF8Data(message)
|
return # FIXME inconsistent
|
||||||
data += json.dumps({
|
subject = shared.fixPotentiallyInvalidUTF8Data(subject)
|
||||||
'msgid': hexlify(msgid),
|
message = shared.fixPotentiallyInvalidUTF8Data(message)
|
||||||
'toAddress': toAddress,
|
return {"inboxMessage": [{
|
||||||
'fromAddress': fromAddress,
|
'msgid': hid,
|
||||||
'subject': base64.b64encode(subject),
|
'toAddress': toAddress,
|
||||||
'message': base64.b64encode(message),
|
'fromAddress': fromAddress,
|
||||||
'encodingType': encodingtype,
|
'subject': base64.b64encode(subject),
|
||||||
'receivedTime': received,
|
'message': base64.b64encode(message),
|
||||||
'read': read}, indent=4, separators=(',', ': '))
|
'encodingType': encodingtype,
|
||||||
data += ']}'
|
'receivedTime': received,
|
||||||
return data
|
'read': read
|
||||||
|
}]}
|
||||||
|
|
||||||
def HandleGetAllSentMessages(self, params):
|
@command('getAllSentMessages')
|
||||||
|
def HandleGetAllSentMessages(self):
|
||||||
"""Handle a request to get all sent messages"""
|
"""Handle a request to get all sent messages"""
|
||||||
|
|
||||||
queryreturn = sqlQuery(
|
queryreturn = sqlQuery(
|
||||||
|
@ -777,15 +691,12 @@ class MySimpleXMLRPCRequestHandler(SimpleXMLRPCRequestHandler):
|
||||||
" message, encodingtype, status, ackdata FROM sent"
|
" message, encodingtype, status, ackdata FROM sent"
|
||||||
" WHERE folder='sent' ORDER BY lastactiontime"
|
" WHERE folder='sent' ORDER BY lastactiontime"
|
||||||
)
|
)
|
||||||
data = '{"sentMessages":['
|
data = []
|
||||||
for row in queryreturn:
|
for (msgid, toAddress, fromAddress, subject, lastactiontime, message,
|
||||||
msgid, toAddress, fromAddress, subject, lastactiontime, message, \
|
encodingtype, status, ackdata) in queryreturn:
|
||||||
encodingtype, status, ackdata = row
|
|
||||||
subject = shared.fixPotentiallyInvalidUTF8Data(subject)
|
subject = shared.fixPotentiallyInvalidUTF8Data(subject)
|
||||||
message = shared.fixPotentiallyInvalidUTF8Data(message)
|
message = shared.fixPotentiallyInvalidUTF8Data(message)
|
||||||
if len(data) > 25:
|
data.append({
|
||||||
data += ','
|
|
||||||
data += json.dumps({
|
|
||||||
'msgid': hexlify(msgid),
|
'msgid': hexlify(msgid),
|
||||||
'toAddress': toAddress,
|
'toAddress': toAddress,
|
||||||
'fromAddress': fromAddress,
|
'fromAddress': fromAddress,
|
||||||
|
@ -794,107 +705,92 @@ class MySimpleXMLRPCRequestHandler(SimpleXMLRPCRequestHandler):
|
||||||
'encodingType': encodingtype,
|
'encodingType': encodingtype,
|
||||||
'lastActionTime': lastactiontime,
|
'lastActionTime': lastactiontime,
|
||||||
'status': status,
|
'status': status,
|
||||||
'ackData': hexlify(ackdata)}, indent=4, separators=(',', ': '))
|
'ackData': hexlify(ackdata)
|
||||||
data += ']}'
|
})
|
||||||
return data
|
return {"sentMessages": data}
|
||||||
|
|
||||||
def HandleGetAllSentMessageIds(self, params):
|
@command('getAllSentMessageIds', 'getAllSentMessageIDs')
|
||||||
|
def HandleGetAllSentMessageIds(self):
|
||||||
"""Handle a request to get all sent message IDs"""
|
"""Handle a request to get all sent message IDs"""
|
||||||
|
|
||||||
queryreturn = sqlQuery(
|
queryreturn = sqlQuery(
|
||||||
"SELECT msgid FROM sent where folder='sent'"
|
"SELECT msgid FROM sent WHERE folder='sent'"
|
||||||
" ORDER BY lastactiontime"
|
" ORDER BY lastactiontime"
|
||||||
)
|
)
|
||||||
data = '{"sentMessageIds":['
|
return {"sentMessageIds": [
|
||||||
for row in queryreturn:
|
{'msgid': hexlify(msgid)} for msgid, in queryreturn
|
||||||
msgid = row[0]
|
]}
|
||||||
if len(data) > 25:
|
|
||||||
data += ','
|
|
||||||
data += json.dumps(
|
|
||||||
{'msgid': hexlify(msgid)}, indent=4, separators=(',', ': '))
|
|
||||||
data += ']}'
|
|
||||||
return data
|
|
||||||
|
|
||||||
def HandleInboxMessagesByReceiver(self, params):
|
# after some time getInboxMessagesByAddress should be removed
|
||||||
|
@command('getInboxMessagesByReceiver', 'getInboxMessagesByAddress')
|
||||||
|
def HandleInboxMessagesByReceiver(self, toAddress):
|
||||||
"""Handle a request to get inbox messages by receiver"""
|
"""Handle a request to get inbox messages by receiver"""
|
||||||
|
|
||||||
if not params:
|
|
||||||
raise APIError(0, 'I need parameters!')
|
|
||||||
toAddress = params[0]
|
|
||||||
queryreturn = sqlQuery(
|
queryreturn = sqlQuery(
|
||||||
"SELECT msgid, toaddress, fromaddress, subject, received, message,"
|
"SELECT msgid, toaddress, fromaddress, subject, received, message,"
|
||||||
" encodingtype FROM inbox WHERE folder='inbox' AND toAddress=?",
|
" encodingtype FROM inbox WHERE folder='inbox' AND toAddress=?",
|
||||||
toAddress)
|
toAddress)
|
||||||
data = '{"inboxMessages":['
|
data = []
|
||||||
for row in queryreturn:
|
for (msgid, toAddress, fromAddress, subject, received, message,
|
||||||
msgid, toAddress, fromAddress, subject, received, message, \
|
encodingtype) in queryreturn:
|
||||||
encodingtype = row
|
|
||||||
subject = shared.fixPotentiallyInvalidUTF8Data(subject)
|
subject = shared.fixPotentiallyInvalidUTF8Data(subject)
|
||||||
message = shared.fixPotentiallyInvalidUTF8Data(message)
|
message = shared.fixPotentiallyInvalidUTF8Data(message)
|
||||||
if len(data) > 25:
|
data.append({
|
||||||
data += ','
|
|
||||||
data += json.dumps({
|
|
||||||
'msgid': hexlify(msgid),
|
'msgid': hexlify(msgid),
|
||||||
'toAddress': toAddress,
|
'toAddress': toAddress,
|
||||||
'fromAddress': fromAddress,
|
'fromAddress': fromAddress,
|
||||||
'subject': base64.b64encode(subject),
|
'subject': base64.b64encode(subject),
|
||||||
'message': base64.b64encode(message),
|
'message': base64.b64encode(message),
|
||||||
'encodingType': encodingtype,
|
'encodingType': encodingtype,
|
||||||
'receivedTime': received}, indent=4, separators=(',', ': '))
|
'receivedTime': received
|
||||||
data += ']}'
|
})
|
||||||
return data
|
return {"inboxMessages": data}
|
||||||
|
|
||||||
def HandleGetSentMessageById(self, params):
|
@command('getSentMessageById', 'getSentMessageByID')
|
||||||
|
def HandleGetSentMessageById(self, hid):
|
||||||
"""Handle a request to get a sent message by ID"""
|
"""Handle a request to get a sent message by ID"""
|
||||||
|
|
||||||
if not params:
|
msgid = self._decode(hid, "hex")
|
||||||
raise APIError(0, 'I need parameters!')
|
|
||||||
msgid = self._decode(params[0], "hex")
|
|
||||||
queryreturn = sqlQuery(
|
queryreturn = sqlQuery(
|
||||||
"SELECT msgid, toaddress, fromaddress, subject, lastactiontime,"
|
"SELECT msgid, toaddress, fromaddress, subject, lastactiontime,"
|
||||||
" message, encodingtype, status, ackdata FROM sent WHERE msgid=?",
|
" message, encodingtype, status, ackdata FROM sent WHERE msgid=?",
|
||||||
msgid
|
msgid
|
||||||
)
|
)
|
||||||
data = '{"sentMessage":['
|
try:
|
||||||
for row in queryreturn:
|
(toAddress, fromAddress, subject, lastactiontime, message,
|
||||||
msgid, toAddress, fromAddress, subject, lastactiontime, message, \
|
encodingtype, status, ackdata) = queryreturn[0]
|
||||||
encodingtype, status, ackdata = row
|
except IndexError:
|
||||||
subject = shared.fixPotentiallyInvalidUTF8Data(subject)
|
return # FIXME inconsistent
|
||||||
message = shared.fixPotentiallyInvalidUTF8Data(message)
|
subject = shared.fixPotentiallyInvalidUTF8Data(subject)
|
||||||
data += json.dumps({
|
message = shared.fixPotentiallyInvalidUTF8Data(message)
|
||||||
'msgid': hexlify(msgid),
|
return {"sentMessage": [{
|
||||||
'toAddress': toAddress,
|
'msgid': hid,
|
||||||
'fromAddress': fromAddress,
|
'toAddress': toAddress,
|
||||||
'subject': base64.b64encode(subject),
|
'fromAddress': fromAddress,
|
||||||
'message': base64.b64encode(message),
|
'subject': base64.b64encode(subject),
|
||||||
'encodingType': encodingtype,
|
'message': base64.b64encode(message),
|
||||||
'lastActionTime': lastactiontime,
|
'encodingType': encodingtype,
|
||||||
'status': status,
|
'lastActionTime': lastactiontime,
|
||||||
'ackData': hexlify(ackdata)}, indent=4, separators=(',', ': '))
|
'status': status,
|
||||||
data += ']}'
|
'ackData': hexlify(ackdata)
|
||||||
return data
|
}]}
|
||||||
|
|
||||||
def HandleGetSentMessagesByAddress(self, params):
|
@command('getSentMessagesByAddress', 'getSentMessagesBySender')
|
||||||
|
def HandleGetSentMessagesByAddress(self, fromAddress):
|
||||||
"""Handle a request to get sent messages by address"""
|
"""Handle a request to get sent messages by address"""
|
||||||
|
|
||||||
if not params:
|
|
||||||
raise APIError(0, 'I need parameters!')
|
|
||||||
fromAddress = params[0]
|
|
||||||
queryreturn = sqlQuery(
|
queryreturn = sqlQuery(
|
||||||
"SELECT msgid, toaddress, fromaddress, subject, lastactiontime,"
|
"SELECT msgid, toaddress, fromaddress, subject, lastactiontime,"
|
||||||
" message, encodingtype, status, ackdata FROM sent"
|
" message, encodingtype, status, ackdata FROM sent"
|
||||||
" WHERE folder='sent' AND fromAddress=? ORDER BY lastactiontime",
|
" WHERE folder='sent' AND fromAddress=? ORDER BY lastactiontime",
|
||||||
fromAddress
|
fromAddress
|
||||||
)
|
)
|
||||||
data = '{"sentMessages":['
|
data = []
|
||||||
for row in queryreturn:
|
for (msgid, toAddress, fromAddress, subject, lastactiontime, message,
|
||||||
msgid, toAddress, fromAddress, subject, lastactiontime, message, \
|
encodingtype, status, ackdata) in queryreturn:
|
||||||
encodingtype, status, ackdata = row
|
|
||||||
subject = shared.fixPotentiallyInvalidUTF8Data(subject)
|
subject = shared.fixPotentiallyInvalidUTF8Data(subject)
|
||||||
message = shared.fixPotentiallyInvalidUTF8Data(message)
|
message = shared.fixPotentiallyInvalidUTF8Data(message)
|
||||||
if len(data) > 25:
|
data.append({
|
||||||
data += ','
|
|
||||||
data += json.dumps({
|
|
||||||
'msgid': hexlify(msgid),
|
'msgid': hexlify(msgid),
|
||||||
'toAddress': toAddress,
|
'toAddress': toAddress,
|
||||||
'fromAddress': fromAddress,
|
'fromAddress': fromAddress,
|
||||||
|
@ -903,91 +799,72 @@ class MySimpleXMLRPCRequestHandler(SimpleXMLRPCRequestHandler):
|
||||||
'encodingType': encodingtype,
|
'encodingType': encodingtype,
|
||||||
'lastActionTime': lastactiontime,
|
'lastActionTime': lastactiontime,
|
||||||
'status': status,
|
'status': status,
|
||||||
'ackData': hexlify(ackdata)}, indent=4, separators=(',', ': '))
|
'ackData': hexlify(ackdata)
|
||||||
data += ']}'
|
})
|
||||||
return data
|
return {"sentMessages": data}
|
||||||
|
|
||||||
def HandleGetSentMessagesByAckData(self, params):
|
@command('getSentMessageByAckData')
|
||||||
|
def HandleGetSentMessagesByAckData(self, ackData):
|
||||||
"""Handle a request to get sent messages by ack data"""
|
"""Handle a request to get sent messages by ack data"""
|
||||||
|
|
||||||
if not params:
|
ackData = self._decode(ackData, "hex")
|
||||||
raise APIError(0, 'I need parameters!')
|
|
||||||
ackData = self._decode(params[0], "hex")
|
|
||||||
queryreturn = sqlQuery(
|
queryreturn = sqlQuery(
|
||||||
"SELECT msgid, toaddress, fromaddress, subject, lastactiontime,"
|
"SELECT msgid, toaddress, fromaddress, subject, lastactiontime,"
|
||||||
" message, encodingtype, status, ackdata FROM sent"
|
" message, encodingtype, status, ackdata FROM sent"
|
||||||
" WHERE ackdata=?", ackData
|
" WHERE ackdata=?", ackData
|
||||||
)
|
)
|
||||||
data = '{"sentMessage":['
|
|
||||||
for row in queryreturn:
|
|
||||||
msgid, toAddress, fromAddress, subject, lastactiontime, message, \
|
|
||||||
encodingtype, status, ackdata = row
|
|
||||||
subject = shared.fixPotentiallyInvalidUTF8Data(subject)
|
|
||||||
message = shared.fixPotentiallyInvalidUTF8Data(message)
|
|
||||||
data += json.dumps({
|
|
||||||
'msgid': hexlify(msgid),
|
|
||||||
'toAddress': toAddress,
|
|
||||||
'fromAddress': fromAddress,
|
|
||||||
'subject': base64.b64encode(subject),
|
|
||||||
'message': base64.b64encode(message),
|
|
||||||
'encodingType': encodingtype,
|
|
||||||
'lastActionTime': lastactiontime,
|
|
||||||
'status': status,
|
|
||||||
'ackData': hexlify(ackdata)}, indent=4, separators=(',', ': '))
|
|
||||||
data += ']}'
|
|
||||||
return data
|
|
||||||
|
|
||||||
def HandleTrashMessage(self, params):
|
try:
|
||||||
|
(msgid, toAddress, fromAddress, subject, lastactiontime,
|
||||||
|
message, encodingtype, status, ackdata) = queryreturn[0]
|
||||||
|
except IndexError:
|
||||||
|
return # FIXME inconsistent
|
||||||
|
subject = shared.fixPotentiallyInvalidUTF8Data(subject)
|
||||||
|
message = shared.fixPotentiallyInvalidUTF8Data(message)
|
||||||
|
return {"sentMessage": [{
|
||||||
|
'msgid': hexlify(msgid),
|
||||||
|
'toAddress': toAddress,
|
||||||
|
'fromAddress': fromAddress,
|
||||||
|
'subject': base64.b64encode(subject),
|
||||||
|
'message': base64.b64encode(message),
|
||||||
|
'encodingType': encodingtype,
|
||||||
|
'lastActionTime': lastactiontime,
|
||||||
|
'status': status,
|
||||||
|
'ackData': hexlify(ackdata)
|
||||||
|
}]}
|
||||||
|
|
||||||
|
@command('trashMessage')
|
||||||
|
def HandleTrashMessage(self, msgid):
|
||||||
"""Handle a request to trash a message by ID"""
|
"""Handle a request to trash a message by ID"""
|
||||||
|
msgid = self._decode(msgid, "hex")
|
||||||
if not params:
|
|
||||||
raise APIError(0, 'I need parameters!')
|
|
||||||
msgid = self._decode(params[0], "hex")
|
|
||||||
|
|
||||||
# Trash if in inbox table
|
# Trash if in inbox table
|
||||||
helper_inbox.trash(msgid)
|
helper_inbox.trash(msgid)
|
||||||
# Trash if in sent table
|
# Trash if in sent table
|
||||||
sqlExecute('''UPDATE sent SET folder='trash' WHERE msgid=?''', msgid)
|
sqlExecute("UPDATE sent SET folder='trash' WHERE msgid=?", msgid)
|
||||||
return 'Trashed message (assuming message existed).'
|
return 'Trashed message (assuming message existed).'
|
||||||
|
|
||||||
def HandleTrashInboxMessage(self, params):
|
@command('trashInboxMessage')
|
||||||
|
def HandleTrashInboxMessage(self, msgid):
|
||||||
"""Handle a request to trash an inbox message by ID"""
|
"""Handle a request to trash an inbox message by ID"""
|
||||||
|
msgid = self._decode(msgid, "hex")
|
||||||
if not params:
|
|
||||||
raise APIError(0, 'I need parameters!')
|
|
||||||
msgid = self._decode(params[0], "hex")
|
|
||||||
helper_inbox.trash(msgid)
|
helper_inbox.trash(msgid)
|
||||||
return 'Trashed inbox message (assuming message existed).'
|
return 'Trashed inbox message (assuming message existed).'
|
||||||
|
|
||||||
def HandleTrashSentMessage(self, params):
|
@command('trashSentMessage')
|
||||||
|
def HandleTrashSentMessage(self, msgid):
|
||||||
"""Handle a request to trash a sent message by ID"""
|
"""Handle a request to trash a sent message by ID"""
|
||||||
|
msgid = self._decode(msgid, "hex")
|
||||||
if not params:
|
|
||||||
raise APIError(0, 'I need parameters!')
|
|
||||||
msgid = self._decode(params[0], "hex")
|
|
||||||
sqlExecute('''UPDATE sent SET folder='trash' WHERE msgid=?''', msgid)
|
sqlExecute('''UPDATE sent SET folder='trash' WHERE msgid=?''', msgid)
|
||||||
return 'Trashed sent message (assuming message existed).'
|
return 'Trashed sent message (assuming message existed).'
|
||||||
|
|
||||||
def HandleSendMessage(self, params): # pylint: disable=too-many-locals
|
@command('sendMessage')
|
||||||
|
def HandleSendMessage(
|
||||||
|
self, toAddress, fromAddress, subject, message,
|
||||||
|
encodingType=2, TTL=4 * 24 * 60 * 60
|
||||||
|
):
|
||||||
"""Handle a request to send a message"""
|
"""Handle a request to send a message"""
|
||||||
|
|
||||||
if not params:
|
if encodingType not in (2, 3):
|
||||||
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.')
|
raise APIError(6, 'The encoding type must be 2 or 3.')
|
||||||
subject = self._decode(subject, "base64")
|
subject = self._decode(subject, "base64")
|
||||||
message = self._decode(message, "base64")
|
message = self._decode(message, "base64")
|
||||||
|
@ -999,8 +876,7 @@ class MySimpleXMLRPCRequestHandler(SimpleXMLRPCRequestHandler):
|
||||||
TTL = 28 * 24 * 60 * 60
|
TTL = 28 * 24 * 60 * 60
|
||||||
toAddress = addBMIfNotPresent(toAddress)
|
toAddress = addBMIfNotPresent(toAddress)
|
||||||
fromAddress = addBMIfNotPresent(fromAddress)
|
fromAddress = addBMIfNotPresent(fromAddress)
|
||||||
status, addressVersionNumber, streamNumber, toRipe = \
|
streamNumber, toRipe = self._verifyAddress(toAddress)[2:]
|
||||||
self._verifyAddress(toAddress)
|
|
||||||
self._verifyAddress(fromAddress)
|
self._verifyAddress(fromAddress)
|
||||||
try:
|
try:
|
||||||
fromAddressEnabled = BMConfigParser().getboolean(
|
fromAddressEnabled = BMConfigParser().getboolean(
|
||||||
|
@ -1035,34 +911,24 @@ class MySimpleXMLRPCRequestHandler(SimpleXMLRPCRequestHandler):
|
||||||
toLabel = ''
|
toLabel = ''
|
||||||
queryreturn = sqlQuery(
|
queryreturn = sqlQuery(
|
||||||
"SELECT label FROM addressbook WHERE address=?", toAddress)
|
"SELECT label FROM addressbook WHERE address=?", toAddress)
|
||||||
if queryreturn != []:
|
try:
|
||||||
for row in queryreturn:
|
toLabel, = queryreturn[0][0]
|
||||||
toLabel, = row
|
except IndexError:
|
||||||
|
pass
|
||||||
|
|
||||||
queues.UISignalQueue.put(('displayNewSentMessage', (
|
queues.UISignalQueue.put(('displayNewSentMessage', (
|
||||||
toAddress, toLabel, fromAddress, subject, message, ackdata)))
|
toAddress, toLabel, fromAddress, subject, message, ackdata)))
|
||||||
|
|
||||||
queues.workerQueue.put(('sendmessage', toAddress))
|
queues.workerQueue.put(('sendmessage', toAddress))
|
||||||
|
|
||||||
return hexlify(ackdata)
|
return hexlify(ackdata)
|
||||||
|
|
||||||
def HandleSendBroadcast(self, params):
|
@command('sendBroadcast')
|
||||||
|
def HandleSendBroadcast(
|
||||||
|
self, fromAddress, subject, message, encodingType=2,
|
||||||
|
TTL=4 * 24 * 60 * 60):
|
||||||
"""Handle a request to send a broadcast message"""
|
"""Handle a request to send a broadcast message"""
|
||||||
|
|
||||||
if not params:
|
if encodingType not in (2, 3):
|
||||||
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.')
|
raise APIError(6, 'The encoding type must be 2 or 3.')
|
||||||
|
|
||||||
subject = self._decode(subject, "base64")
|
subject = self._decode(subject, "base64")
|
||||||
|
@ -1082,7 +948,7 @@ class MySimpleXMLRPCRequestHandler(SimpleXMLRPCRequestHandler):
|
||||||
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 = '[Broadcast subscribers]'
|
toAddress = str_broadcast_subscribers
|
||||||
ripe = ''
|
ripe = ''
|
||||||
|
|
||||||
t = ('',
|
t = ('',
|
||||||
|
@ -1102,55 +968,45 @@ class MySimpleXMLRPCRequestHandler(SimpleXMLRPCRequestHandler):
|
||||||
TTL)
|
TTL)
|
||||||
helper_sent.insert(t)
|
helper_sent.insert(t)
|
||||||
|
|
||||||
toLabel = '[Broadcast subscribers]'
|
toLabel = str_broadcast_subscribers
|
||||||
queues.UISignalQueue.put(('displayNewSentMessage', (
|
queues.UISignalQueue.put(('displayNewSentMessage', (
|
||||||
toAddress, toLabel, fromAddress, subject, message, ackdata)))
|
toAddress, toLabel, fromAddress, subject, message, ackdata)))
|
||||||
queues.workerQueue.put(('sendbroadcast', ''))
|
queues.workerQueue.put(('sendbroadcast', ''))
|
||||||
|
|
||||||
return hexlify(ackdata)
|
return hexlify(ackdata)
|
||||||
|
|
||||||
def HandleGetStatus(self, params):
|
@command('getStatus')
|
||||||
|
def HandleGetStatus(self, ackdata):
|
||||||
"""Handle a request to get the status of a sent message"""
|
"""Handle a request to get the status of a sent message"""
|
||||||
|
|
||||||
if len(params) != 1:
|
|
||||||
raise APIError(0, 'I need one parameter!')
|
|
||||||
ackdata, = params
|
|
||||||
if len(ackdata) < 76:
|
if len(ackdata) < 76:
|
||||||
# The length of ackData should be at least 38 bytes (76 hex digits)
|
# The length of ackData should be at least 38 bytes (76 hex digits)
|
||||||
raise APIError(15, 'Invalid ackData object size.')
|
raise APIError(15, 'Invalid ackData object size.')
|
||||||
ackdata = self._decode(ackdata, "hex")
|
ackdata = self._decode(ackdata, "hex")
|
||||||
queryreturn = sqlQuery(
|
queryreturn = sqlQuery(
|
||||||
"SELECT status FROM sent where ackdata=?", ackdata)
|
"SELECT status FROM sent where ackdata=?", ackdata)
|
||||||
if queryreturn == []:
|
try:
|
||||||
|
return queryreturn[0][0]
|
||||||
|
except IndexError:
|
||||||
return 'notfound'
|
return 'notfound'
|
||||||
for row in queryreturn:
|
|
||||||
status, = row
|
|
||||||
return status
|
|
||||||
|
|
||||||
def HandleAddSubscription(self, params):
|
@command('addSubscription')
|
||||||
|
def HandleAddSubscription(self, address, label=''):
|
||||||
"""Handle a request to add a subscription"""
|
"""Handle a request to add a subscription"""
|
||||||
|
|
||||||
if not params:
|
if label:
|
||||||
raise APIError(0, 'I need parameters!')
|
|
||||||
if len(params) == 1:
|
|
||||||
address, = params
|
|
||||||
label = ''
|
|
||||||
if len(params) == 2:
|
|
||||||
address, label = params
|
|
||||||
label = self._decode(label, "base64")
|
label = self._decode(label, "base64")
|
||||||
try:
|
try:
|
||||||
unicode(label, 'utf-8')
|
unicode(label, 'utf-8')
|
||||||
except BaseException:
|
except UnicodeDecodeError:
|
||||||
raise APIError(17, 'Label is not valid UTF-8 data.')
|
raise APIError(17, 'Label is not valid UTF-8 data.')
|
||||||
if len(params) > 2:
|
|
||||||
raise APIError(0, 'I need either 1 or 2 parameters!')
|
|
||||||
address = addBMIfNotPresent(address)
|
|
||||||
self._verifyAddress(address)
|
self._verifyAddress(address)
|
||||||
|
address = addBMIfNotPresent(address)
|
||||||
# First we must check to see if the address is already in the
|
# First we must check to see if the address is already in the
|
||||||
# subscriptions list.
|
# subscriptions list.
|
||||||
queryreturn = sqlQuery(
|
queryreturn = sqlQuery(
|
||||||
"SELECT * FROM subscriptions WHERE address=?", address)
|
"SELECT * FROM subscriptions WHERE address=?", address)
|
||||||
if queryreturn != []:
|
if queryreturn:
|
||||||
raise APIError(16, 'You are already subscribed to that address.')
|
raise APIError(16, 'You are already subscribed to that address.')
|
||||||
sqlExecute(
|
sqlExecute(
|
||||||
"INSERT INTO subscriptions VALUES (?,?,?)", label, address, True)
|
"INSERT INTO subscriptions VALUES (?,?,?)", label, address, True)
|
||||||
|
@ -1159,36 +1015,37 @@ class MySimpleXMLRPCRequestHandler(SimpleXMLRPCRequestHandler):
|
||||||
queues.UISignalQueue.put(('rerenderSubscriptions', ''))
|
queues.UISignalQueue.put(('rerenderSubscriptions', ''))
|
||||||
return 'Added subscription.'
|
return 'Added subscription.'
|
||||||
|
|
||||||
def HandleDeleteSubscription(self, params):
|
@command('deleteSubscription')
|
||||||
|
def HandleDeleteSubscription(self, address):
|
||||||
"""Handle a request to delete a subscription"""
|
"""Handle a request to delete a subscription"""
|
||||||
|
|
||||||
if len(params) != 1:
|
|
||||||
raise APIError(0, 'I need 1 parameter!')
|
|
||||||
address, = params
|
|
||||||
address = addBMIfNotPresent(address)
|
address = addBMIfNotPresent(address)
|
||||||
sqlExecute('''DELETE FROM subscriptions WHERE address=?''', address)
|
sqlExecute("DELETE FROM subscriptions WHERE address=?", address)
|
||||||
shared.reloadBroadcastSendersForWhichImWatching()
|
shared.reloadBroadcastSendersForWhichImWatching()
|
||||||
queues.UISignalQueue.put(('rerenderMessagelistFromLabels', ''))
|
queues.UISignalQueue.put(('rerenderMessagelistFromLabels', ''))
|
||||||
queues.UISignalQueue.put(('rerenderSubscriptions', ''))
|
queues.UISignalQueue.put(('rerenderSubscriptions', ''))
|
||||||
return 'Deleted subscription if it existed.'
|
return 'Deleted subscription if it existed.'
|
||||||
|
|
||||||
def ListSubscriptions(self, params):
|
@command('listSubscriptions')
|
||||||
|
def ListSubscriptions(self):
|
||||||
"""Handle a request to list susbcriptions"""
|
"""Handle a request to list susbcriptions"""
|
||||||
|
|
||||||
queryreturn = sqlQuery(
|
queryreturn = sqlQuery(
|
||||||
"SELECT label, address, enabled FROM subscriptions")
|
"SELECT label, address, enabled FROM subscriptions")
|
||||||
data = {'subscriptions': []}
|
data = []
|
||||||
for row in queryreturn:
|
for label, address, enabled in queryreturn:
|
||||||
label, address, enabled = row
|
|
||||||
label = shared.fixPotentiallyInvalidUTF8Data(label)
|
label = shared.fixPotentiallyInvalidUTF8Data(label)
|
||||||
data['subscriptions'].append({
|
data.append({
|
||||||
'label': base64.b64encode(label),
|
'label': base64.b64encode(label),
|
||||||
'address': address,
|
'address': address,
|
||||||
'enabled': enabled == 1
|
'enabled': enabled == 1
|
||||||
})
|
})
|
||||||
return json.dumps(data, indent=4, separators=(',', ': '))
|
return {'subscriptions': data}
|
||||||
|
|
||||||
def HandleDisseminatePreEncryptedMsg(self, params):
|
@command('disseminatePreEncryptedMsg')
|
||||||
|
def HandleDisseminatePreEncryptedMsg(
|
||||||
|
self, encryptedPayload, requiredAverageProofOfWorkNonceTrialsPerByte,
|
||||||
|
requiredPayloadLengthExtraBytes):
|
||||||
"""Handle a request to disseminate an encrypted message"""
|
"""Handle a request to disseminate an encrypted message"""
|
||||||
|
|
||||||
# The device issuing this command to PyBitmessage supplies a msg
|
# The device issuing this command to PyBitmessage supplies a msg
|
||||||
|
@ -1196,42 +1053,28 @@ class MySimpleXMLRPCRequestHandler(SimpleXMLRPCRequestHandler):
|
||||||
# to be done. PyBitmessage accepts this msg object and sends it out
|
# to be done. PyBitmessage accepts this msg object and sends it out
|
||||||
# to the rest of the Bitmessage network as if it had generated
|
# to the rest of the Bitmessage network as if it had generated
|
||||||
# the message itself. Please do not yet add this to the api doc.
|
# the message itself. Please do not yet add this to the api doc.
|
||||||
if len(params) != 3:
|
|
||||||
raise APIError(0, 'I need 3 parameter!')
|
|
||||||
encryptedPayload, requiredAverageProofOfWorkNonceTrialsPerByte, \
|
|
||||||
requiredPayloadLengthExtraBytes = params
|
|
||||||
encryptedPayload = self._decode(encryptedPayload, "hex")
|
encryptedPayload = self._decode(encryptedPayload, "hex")
|
||||||
# Let us do the POW and attach it to the front
|
# Let us do the POW and attach it to the front
|
||||||
target = 2**64 / (
|
target = 2**64 / ((
|
||||||
(
|
len(encryptedPayload) + requiredPayloadLengthExtraBytes + 8) *
|
||||||
len(encryptedPayload) + requiredPayloadLengthExtraBytes + 8
|
requiredAverageProofOfWorkNonceTrialsPerByte)
|
||||||
) * requiredAverageProofOfWorkNonceTrialsPerByte
|
logger.info(
|
||||||
|
'(For msg message via API) Doing proof of work. Total required'
|
||||||
|
' difficulty: %s\nRequired small message difficulty: %s',
|
||||||
|
float(requiredAverageProofOfWorkNonceTrialsPerByte) /
|
||||||
|
defaults.networkDefaultProofOfWorkNonceTrialsPerByte,
|
||||||
|
float(requiredPayloadLengthExtraBytes) /
|
||||||
|
defaults.networkDefaultPayloadLengthExtraBytes,
|
||||||
)
|
)
|
||||||
with threads.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,
|
|
||||||
)
|
|
||||||
powStartTime = time.time()
|
powStartTime = time.time()
|
||||||
initialHash = hashlib.sha512(encryptedPayload).digest()
|
initialHash = hashlib.sha512(encryptedPayload).digest()
|
||||||
trialValue, nonce = proofofwork.run(target, initialHash)
|
trialValue, nonce = proofofwork.run(target, initialHash)
|
||||||
with threads.printLock:
|
logger.info(
|
||||||
print '(For msg message via API) Found proof of work', trialValue, 'Nonce:', nonce
|
'(For msg message via API) Found proof of work %s\nNonce: %s\n'
|
||||||
try:
|
'POW took %s seconds. %s nonce trials per second.',
|
||||||
print(
|
trialValue, nonce, int(time.time() - powStartTime),
|
||||||
'POW took', int(time.time() - powStartTime),
|
nonce / (time.time() - powStartTime)
|
||||||
'seconds.', nonce / (time.time() - powStartTime),
|
)
|
||||||
'nonce trials per second.',
|
|
||||||
)
|
|
||||||
except BaseException:
|
|
||||||
pass
|
|
||||||
encryptedPayload = pack('>Q', nonce) + encryptedPayload
|
encryptedPayload = pack('>Q', nonce) + encryptedPayload
|
||||||
toStreamNumber = decodeVarint(encryptedPayload[16:26])[0]
|
toStreamNumber = decodeVarint(encryptedPayload[16:26])[0]
|
||||||
inventoryHash = calculateInventoryHash(encryptedPayload)
|
inventoryHash = calculateInventoryHash(encryptedPayload)
|
||||||
|
@ -1241,21 +1084,21 @@ class MySimpleXMLRPCRequestHandler(SimpleXMLRPCRequestHandler):
|
||||||
objectType, toStreamNumber, encryptedPayload,
|
objectType, toStreamNumber, encryptedPayload,
|
||||||
int(time.time()) + TTL, ''
|
int(time.time()) + TTL, ''
|
||||||
)
|
)
|
||||||
with threads.printLock:
|
logger.info(
|
||||||
print 'Broadcasting inv for msg(API disseminatePreEncryptedMsg command):', hexlify(inventoryHash)
|
'Broadcasting inv for msg(API disseminatePreEncryptedMsg'
|
||||||
|
' command): %s', hexlify(inventoryHash))
|
||||||
queues.invQueue.put((toStreamNumber, inventoryHash))
|
queues.invQueue.put((toStreamNumber, inventoryHash))
|
||||||
|
|
||||||
def HandleTrashSentMessageByAckDAta(self, params):
|
@command('trashSentMessageByAckData')
|
||||||
|
def HandleTrashSentMessageByAckDAta(self, ackdata):
|
||||||
"""Handle a request to trash a sent message by ackdata"""
|
"""Handle a request to trash a sent message by ackdata"""
|
||||||
|
|
||||||
# This API method should only be used when msgid is not available
|
# This API method should only be used when msgid is not available
|
||||||
if not params:
|
ackdata = self._decode(ackdata, "hex")
|
||||||
raise APIError(0, 'I need parameters!')
|
|
||||||
ackdata = self._decode(params[0], "hex")
|
|
||||||
sqlExecute("UPDATE sent SET folder='trash' WHERE ackdata=?", ackdata)
|
sqlExecute("UPDATE sent SET folder='trash' WHERE ackdata=?", ackdata)
|
||||||
return 'Trashed sent message (assuming message existed).'
|
return 'Trashed sent message (assuming message existed).'
|
||||||
|
|
||||||
def HandleDissimatePubKey(self, params):
|
@command('disseminatePubkey')
|
||||||
|
def HandleDissimatePubKey(self, payload):
|
||||||
"""Handle a request to disseminate a public key"""
|
"""Handle a request to disseminate a public key"""
|
||||||
|
|
||||||
# The device issuing this command to PyBitmessage supplies a pubkey
|
# The device issuing this command to PyBitmessage supplies a pubkey
|
||||||
|
@ -1263,19 +1106,19 @@ class MySimpleXMLRPCRequestHandler(SimpleXMLRPCRequestHandler):
|
||||||
# PyBitmessage accepts this pubkey object and sends it out to the rest
|
# PyBitmessage accepts this pubkey object and sends it out to the rest
|
||||||
# of the Bitmessage network as if it had generated the pubkey object
|
# of the Bitmessage network as if it had generated the pubkey object
|
||||||
# itself. Please do not yet add this to the api doc.
|
# itself. Please do not yet add this to the api doc.
|
||||||
if len(params) != 1:
|
|
||||||
raise APIError(0, 'I need 1 parameter!')
|
|
||||||
payload, = params
|
|
||||||
payload = self._decode(payload, "hex")
|
payload = self._decode(payload, "hex")
|
||||||
|
|
||||||
# Let us do the POW
|
# Let us do the POW
|
||||||
target = 2 ** 64 / ((
|
target = 2 ** 64 / ((
|
||||||
len(payload) + defaults.networkDefaultPayloadLengthExtraBytes + 8
|
len(payload) + defaults.networkDefaultPayloadLengthExtraBytes + 8
|
||||||
) * defaults.networkDefaultProofOfWorkNonceTrialsPerByte)
|
) * defaults.networkDefaultProofOfWorkNonceTrialsPerByte)
|
||||||
print '(For pubkey message via API) Doing proof of work...'
|
logger.info('(For pubkey message via API) Doing proof of work...')
|
||||||
initialHash = hashlib.sha512(payload).digest()
|
initialHash = hashlib.sha512(payload).digest()
|
||||||
trialValue, nonce = proofofwork.run(target, initialHash)
|
trialValue, nonce = proofofwork.run(target, initialHash)
|
||||||
print '(For pubkey message via API) Found proof of work', trialValue, 'Nonce:', nonce
|
logger.info(
|
||||||
|
'(For pubkey message via API) Found proof of work %s Nonce: %s',
|
||||||
|
trialValue, nonce
|
||||||
|
)
|
||||||
payload = pack('>Q', nonce) + payload
|
payload = pack('>Q', nonce) + payload
|
||||||
|
|
||||||
pubkeyReadPosition = 8 # bypass the nonce
|
pubkeyReadPosition = 8 # bypass the nonce
|
||||||
|
@ -1284,8 +1127,8 @@ class MySimpleXMLRPCRequestHandler(SimpleXMLRPCRequestHandler):
|
||||||
pubkeyReadPosition += 8
|
pubkeyReadPosition += 8
|
||||||
else:
|
else:
|
||||||
pubkeyReadPosition += 4
|
pubkeyReadPosition += 4
|
||||||
addressVersion, addressVersionLength = decodeVarint(
|
addressVersionLength = decodeVarint(
|
||||||
payload[pubkeyReadPosition:pubkeyReadPosition + 10])
|
payload[pubkeyReadPosition:pubkeyReadPosition + 10])[1]
|
||||||
pubkeyReadPosition += addressVersionLength
|
pubkeyReadPosition += addressVersionLength
|
||||||
pubkeyStreamNumber = decodeVarint(
|
pubkeyStreamNumber = decodeVarint(
|
||||||
payload[pubkeyReadPosition:pubkeyReadPosition + 10])[0]
|
payload[pubkeyReadPosition:pubkeyReadPosition + 10])[0]
|
||||||
|
@ -1295,19 +1138,19 @@ class MySimpleXMLRPCRequestHandler(SimpleXMLRPCRequestHandler):
|
||||||
Inventory()[inventoryHash] = (
|
Inventory()[inventoryHash] = (
|
||||||
objectType, pubkeyStreamNumber, payload, int(time.time()) + TTL, ''
|
objectType, pubkeyStreamNumber, payload, int(time.time()) + TTL, ''
|
||||||
)
|
)
|
||||||
with threads.printLock:
|
logger.info(
|
||||||
print 'broadcasting inv within API command disseminatePubkey with hash:', hexlify(inventoryHash)
|
'broadcasting inv within API command disseminatePubkey with'
|
||||||
|
' hash: %s', hexlify(inventoryHash))
|
||||||
queues.invQueue.put((pubkeyStreamNumber, inventoryHash))
|
queues.invQueue.put((pubkeyStreamNumber, inventoryHash))
|
||||||
|
|
||||||
def HandleGetMessageDataByDestinationHash(self, params):
|
@command(
|
||||||
|
'getMessageDataByDestinationHash', 'getMessageDataByDestinationTag')
|
||||||
|
def HandleGetMessageDataByDestinationHash(self, requestedHash):
|
||||||
"""Handle a request to get message data by destination hash"""
|
"""Handle a request to get message data by destination hash"""
|
||||||
|
|
||||||
# Method will eventually be used by a particular Android app to
|
# Method will eventually be used by a particular Android app to
|
||||||
# select relevant messages. Do not yet add this to the api
|
# select relevant messages. Do not yet add this to the api
|
||||||
# doc.
|
# doc.
|
||||||
if len(params) != 1:
|
|
||||||
raise APIError(0, 'I need 1 parameter!')
|
|
||||||
requestedHash, = params
|
|
||||||
if len(requestedHash) != 32:
|
if len(requestedHash) != 32:
|
||||||
raise APIError(
|
raise APIError(
|
||||||
19, 'The length of hash should be 32 bytes (encoded in hex'
|
19, 'The length of hash should be 32 bytes (encoded in hex'
|
||||||
|
@ -1321,8 +1164,7 @@ class MySimpleXMLRPCRequestHandler(SimpleXMLRPCRequestHandler):
|
||||||
"SELECT hash, payload FROM inventory WHERE tag = ''"
|
"SELECT hash, payload FROM inventory WHERE tag = ''"
|
||||||
" and objecttype = 2")
|
" and objecttype = 2")
|
||||||
with SqlBulkExecute() as sql:
|
with SqlBulkExecute() as sql:
|
||||||
for row in queryreturn:
|
for hash01, payload in queryreturn:
|
||||||
hash01, payload = row
|
|
||||||
readPosition = 16 # Nonce length + time length
|
readPosition = 16 # Nonce length + time length
|
||||||
# Stream Number length
|
# Stream Number length
|
||||||
readPosition += decodeVarint(
|
readPosition += decodeVarint(
|
||||||
|
@ -1332,17 +1174,12 @@ class MySimpleXMLRPCRequestHandler(SimpleXMLRPCRequestHandler):
|
||||||
|
|
||||||
queryreturn = sqlQuery(
|
queryreturn = sqlQuery(
|
||||||
"SELECT payload FROM inventory WHERE tag = ?", requestedHash)
|
"SELECT payload FROM inventory WHERE tag = ?", requestedHash)
|
||||||
data = '{"receivedMessageDatas":['
|
return {"receivedMessageDatas": [
|
||||||
for row in queryreturn:
|
{'data': hexlify(payload)} for payload, in queryreturn
|
||||||
payload, = row
|
]}
|
||||||
if len(data) > 25:
|
|
||||||
data += ','
|
|
||||||
data += json.dumps(
|
|
||||||
{'data': hexlify(payload)}, indent=4, separators=(',', ': '))
|
|
||||||
data += ']}'
|
|
||||||
return data
|
|
||||||
|
|
||||||
def HandleClientStatus(self, params):
|
@command('clientStatus')
|
||||||
|
def HandleClientStatus(self):
|
||||||
"""Handle a request to get the status of the client"""
|
"""Handle a request to get the status of the client"""
|
||||||
|
|
||||||
connections_num = len(network.stats.connectedHostsList())
|
connections_num = len(network.stats.connectedHostsList())
|
||||||
|
@ -1352,7 +1189,7 @@ class MySimpleXMLRPCRequestHandler(SimpleXMLRPCRequestHandler):
|
||||||
networkStatus = 'connectedAndReceivingIncomingConnections'
|
networkStatus = 'connectedAndReceivingIncomingConnections'
|
||||||
else:
|
else:
|
||||||
networkStatus = 'connectedButHaveNotReceivedIncomingConnections'
|
networkStatus = 'connectedButHaveNotReceivedIncomingConnections'
|
||||||
return json.dumps({
|
return {
|
||||||
'networkConnections': connections_num,
|
'networkConnections': connections_num,
|
||||||
'numberOfMessagesProcessed': state.numberOfMessagesProcessed,
|
'numberOfMessagesProcessed': state.numberOfMessagesProcessed,
|
||||||
'numberOfBroadcastsProcessed': state.numberOfBroadcastsProcessed,
|
'numberOfBroadcastsProcessed': state.numberOfBroadcastsProcessed,
|
||||||
|
@ -1360,123 +1197,57 @@ class MySimpleXMLRPCRequestHandler(SimpleXMLRPCRequestHandler):
|
||||||
'networkStatus': networkStatus,
|
'networkStatus': networkStatus,
|
||||||
'softwareName': 'PyBitmessage',
|
'softwareName': 'PyBitmessage',
|
||||||
'softwareVersion': softwareVersion
|
'softwareVersion': softwareVersion
|
||||||
}, indent=4, separators=(',', ': '))
|
}
|
||||||
|
|
||||||
def HandleDecodeAddress(self, params):
|
@command('decodeAddress')
|
||||||
|
def HandleDecodeAddress(self, address):
|
||||||
"""Handle a request to decode an address"""
|
"""Handle a request to decode an address"""
|
||||||
|
|
||||||
# Return a meaningful decoding of an address.
|
# Return a meaningful decoding of an address.
|
||||||
if len(params) != 1:
|
|
||||||
raise APIError(0, 'I need 1 parameter!')
|
|
||||||
address, = params
|
|
||||||
status, addressVersion, streamNumber, ripe = decodeAddress(address)
|
status, addressVersion, streamNumber, ripe = decodeAddress(address)
|
||||||
return json.dumps({
|
return {
|
||||||
'status': status,
|
'status': status,
|
||||||
'addressVersion': addressVersion,
|
'addressVersion': addressVersion,
|
||||||
'streamNumber': streamNumber,
|
'streamNumber': streamNumber,
|
||||||
'ripe': base64.b64encode(ripe)
|
'ripe': base64.b64encode(ripe)
|
||||||
}, indent=4, separators=(',', ': '))
|
}
|
||||||
|
|
||||||
def HandleHelloWorld(self, params):
|
@command('helloWorld')
|
||||||
|
def HandleHelloWorld(self, a, b):
|
||||||
"""Test two string params"""
|
"""Test two string params"""
|
||||||
|
|
||||||
a, b = params
|
|
||||||
return a + '-' + b
|
return a + '-' + b
|
||||||
|
|
||||||
def HandleAdd(self, params):
|
@command('add')
|
||||||
|
def HandleAdd(self, a, b):
|
||||||
"""Test two numeric params"""
|
"""Test two numeric params"""
|
||||||
|
|
||||||
a, b = params
|
|
||||||
return a + b
|
return a + b
|
||||||
|
|
||||||
def HandleStatusBar(self, params):
|
@command('statusBar')
|
||||||
|
def HandleStatusBar(self, message):
|
||||||
"""Handle a request to update the status bar"""
|
"""Handle a request to update the status bar"""
|
||||||
|
|
||||||
message, = params
|
|
||||||
queues.UISignalQueue.put(('updateStatusBar', message))
|
queues.UISignalQueue.put(('updateStatusBar', message))
|
||||||
|
|
||||||
def HandleDeleteAndVacuum(self, params):
|
@command('deleteAndVacuum')
|
||||||
|
def HandleDeleteAndVacuum(self):
|
||||||
"""Handle a request to run the deleteandvacuum stored procedure"""
|
"""Handle a request to run the deleteandvacuum stored procedure"""
|
||||||
|
sqlStoredProcedure('deleteandvacuume')
|
||||||
|
return 'done'
|
||||||
|
|
||||||
if not params:
|
@command('shutdown')
|
||||||
sqlStoredProcedure('deleteandvacuume')
|
def HandleShutdown(self):
|
||||||
return 'done'
|
|
||||||
return None
|
|
||||||
|
|
||||||
def HandleShutdown(self, params):
|
|
||||||
"""Handle a request to shutdown the node"""
|
"""Handle a request to shutdown the node"""
|
||||||
|
# backward compatible trick because False == 0 is True
|
||||||
if not params:
|
state.shutdown = False
|
||||||
# backward compatible trick because False == 0 is True
|
return 'done'
|
||||||
state.shutdown = False
|
|
||||||
return 'done'
|
|
||||||
return None
|
|
||||||
|
|
||||||
handlers = {}
|
|
||||||
handlers['helloWorld'] = HandleHelloWorld
|
|
||||||
handlers['add'] = HandleAdd
|
|
||||||
handlers['statusBar'] = HandleStatusBar
|
|
||||||
handlers['listAddresses'] = HandleListAddresses
|
|
||||||
handlers['listAddressBookEntries'] = HandleListAddressBookEntries
|
|
||||||
# the listAddressbook alias should be removed eventually.
|
|
||||||
handlers['listAddressbook'] = HandleListAddressBookEntries
|
|
||||||
handlers['addAddressBookEntry'] = HandleAddAddressBookEntry
|
|
||||||
# the addAddressbook alias should be deleted eventually.
|
|
||||||
handlers['addAddressbook'] = HandleAddAddressBookEntry
|
|
||||||
handlers['deleteAddressBookEntry'] = HandleDeleteAddressBookEntry
|
|
||||||
# The deleteAddressbook alias should be deleted eventually.
|
|
||||||
handlers['deleteAddressbook'] = HandleDeleteAddressBookEntry
|
|
||||||
handlers['createRandomAddress'] = HandleCreateRandomAddress
|
|
||||||
handlers['createDeterministicAddresses'] = \
|
|
||||||
HandleCreateDeterministicAddresses
|
|
||||||
handlers['getDeterministicAddress'] = HandleGetDeterministicAddress
|
|
||||||
handlers['createChan'] = HandleCreateChan
|
|
||||||
handlers['joinChan'] = HandleJoinChan
|
|
||||||
handlers['leaveChan'] = HandleLeaveChan
|
|
||||||
handlers['deleteAddress'] = HandleDeleteAddress
|
|
||||||
handlers['getAllInboxMessages'] = HandleGetAllInboxMessages
|
|
||||||
handlers['getAllInboxMessageIds'] = HandleGetAllInboxMessageIds
|
|
||||||
handlers['getAllInboxMessageIDs'] = HandleGetAllInboxMessageIds
|
|
||||||
handlers['getInboxMessageById'] = HandleGetInboxMessageById
|
|
||||||
handlers['getInboxMessageByID'] = HandleGetInboxMessageById
|
|
||||||
handlers['getAllSentMessages'] = HandleGetAllSentMessages
|
|
||||||
handlers['getAllSentMessageIds'] = HandleGetAllSentMessageIds
|
|
||||||
handlers['getAllSentMessageIDs'] = HandleGetAllSentMessageIds
|
|
||||||
handlers['getInboxMessagesByReceiver'] = HandleInboxMessagesByReceiver
|
|
||||||
# after some time getInboxMessagesByAddress should be removed
|
|
||||||
handlers['getInboxMessagesByAddress'] = HandleInboxMessagesByReceiver
|
|
||||||
handlers['getSentMessageById'] = HandleGetSentMessageById
|
|
||||||
handlers['getSentMessageByID'] = HandleGetSentMessageById
|
|
||||||
handlers['getSentMessagesByAddress'] = HandleGetSentMessagesByAddress
|
|
||||||
handlers['getSentMessagesBySender'] = HandleGetSentMessagesByAddress
|
|
||||||
handlers['getSentMessageByAckData'] = HandleGetSentMessagesByAckData
|
|
||||||
handlers['trashMessage'] = HandleTrashMessage
|
|
||||||
handlers['trashInboxMessage'] = HandleTrashInboxMessage
|
|
||||||
handlers['trashSentMessage'] = HandleTrashSentMessage
|
|
||||||
handlers['trashSentMessageByAckData'] = HandleTrashSentMessageByAckDAta
|
|
||||||
handlers['sendMessage'] = HandleSendMessage
|
|
||||||
handlers['sendBroadcast'] = HandleSendBroadcast
|
|
||||||
handlers['getStatus'] = HandleGetStatus
|
|
||||||
handlers['addSubscription'] = HandleAddSubscription
|
|
||||||
handlers['deleteSubscription'] = HandleDeleteSubscription
|
|
||||||
handlers['listSubscriptions'] = ListSubscriptions
|
|
||||||
handlers['disseminatePreEncryptedMsg'] = HandleDisseminatePreEncryptedMsg
|
|
||||||
handlers['disseminatePubkey'] = HandleDissimatePubKey
|
|
||||||
handlers['getMessageDataByDestinationHash'] = \
|
|
||||||
HandleGetMessageDataByDestinationHash
|
|
||||||
handlers['getMessageDataByDestinationTag'] = \
|
|
||||||
HandleGetMessageDataByDestinationHash
|
|
||||||
handlers['clientStatus'] = HandleClientStatus
|
|
||||||
handlers['decodeAddress'] = HandleDecodeAddress
|
|
||||||
handlers['deleteAndVacuum'] = HandleDeleteAndVacuum
|
|
||||||
handlers['shutdown'] = HandleShutdown
|
|
||||||
|
|
||||||
def _handle_request(self, method, params):
|
def _handle_request(self, method, params):
|
||||||
if method not in self.handlers:
|
try:
|
||||||
|
# pylint: disable=attribute-defined-outside-init
|
||||||
|
self._method = method
|
||||||
|
return self._handlers[method](self, *params)
|
||||||
|
except KeyError:
|
||||||
raise APIError(20, 'Invalid method: %s' % method)
|
raise APIError(20, 'Invalid method: %s' % method)
|
||||||
result = self.handlers[method](self, params)
|
finally:
|
||||||
state.last_api_response = time.time()
|
state.last_api_response = time.time()
|
||||||
return result
|
|
||||||
|
|
||||||
def _dispatch(self, method, params):
|
def _dispatch(self, method, params):
|
||||||
# pylint: disable=attribute-defined-outside-init
|
# pylint: disable=attribute-defined-outside-init
|
||||||
|
|
Reference in New Issue
Block a user