From e7e41ed56bf64bb62ac2d74a77e1eb369ada4ca8 Mon Sep 17 00:00:00 2001 From: Brian Martin Date: Fri, 22 Feb 2019 00:09:19 -0500 Subject: [PATCH] add black and format --- Pipfile | 1 + bumper.py | 109 ++++--- bumper/__init__.py | 87 ++++-- bumper/confserver.py | 688 +++++++++++++++++++++++++---------------- bumper/mqttserver.py | 437 ++++++++++++++------------ bumper/xmppserver.py | 716 ++++++++++++++++++++++++++----------------- examples/sucks.py | 13 +- 7 files changed, 1241 insertions(+), 810 deletions(-) diff --git a/Pipfile b/Pipfile index d5d8bc7..012b6da 100644 --- a/Pipfile +++ b/Pipfile @@ -6,5 +6,6 @@ name = "pypi" [packages] hbmqtt = "*" aiohttp = "*" +black = "*" [dev-packages] diff --git a/bumper.py b/bumper.py index c646c59..c47426e 100644 --- a/bumper.py +++ b/bumper.py @@ -9,57 +9,89 @@ import platform def main(): args = sys.argv - - if len(args) > 0: - if '--debug' in args: - logging.basicConfig(level=logging.DEBUG, - format="[%(asctime)s] :: %(levelname)s :: %(name)s :: %(module)s :: %(funcName)s :: %(lineno)d :: %(message)s") - else: - logging.basicConfig(level=logging.INFO, - format="[%(asctime)s] :: %(levelname)s :: %(name)s :: %(message)s") - #format="[%(asctime)s] :: %(levelname)s :: %(name)s :: %(module)s :: %(funcName)s :: %(lineno)d :: %(message)s") - if platform.system() == "Darwin": #If a Mac, use 0.0.0.0 for listening + if len(args) > 0: + if "--debug" in args: + logging.basicConfig( + level=logging.DEBUG, + format="[%(asctime)s] :: %(levelname)s :: %(name)s :: %(module)s :: %(funcName)s :: %(lineno)d :: %(message)s", + ) + else: + logging.basicConfig( + level=logging.INFO, + format="[%(asctime)s] :: %(levelname)s :: %(name)s :: %(message)s", + ) + # format="[%(asctime)s] :: %(levelname)s :: %(name)s :: %(module)s :: %(funcName)s :: %(lineno)d :: %(message)s") + + if platform.system() == "Darwin": # If a Mac, use 0.0.0.0 for listening listen_host = "0.0.0.0" else: listen_host = socket.gethostbyname(socket.gethostname()) - #listen_host = "localhost" #Try this if the above doesn't work + # listen_host = "localhost" #Try this if the above doesn't work conf_address_443 = (listen_host, 443) conf_address_8007 = (listen_host, 8007) xmpp_address = (listen_host, 5223) mqtt_address = (listen_host, 8883) - - xmpp_server = bumper.XMPPServer(xmpp_address, bumper_users=bumper.bumper_users_var, bumper_bots=bumper.bumper_bots_var,bumper_clients=bumper.bumper_clients_var) - mqtt_server = bumper.MQTTServer(mqtt_address,bumper_users=bumper.bumper_users_var, bumper_bots=bumper.bumper_bots_var,bumper_clients=bumper.bumper_clients_var) - mqtt_helperbot = bumper.MQTTHelperBot(mqtt_address, bumper_bots=bumper.bumper_bots_var,bumper_clients=bumper.bumper_clients_var) - conf_server = bumper.ConfServer(conf_address_443, usessl=True,bumper_users=bumper.bumper_users_var, bumper_bots=bumper.bumper_bots_var,bumper_clients=bumper.bumper_clients_var,helperbot=mqtt_helperbot) - conf_server_2 = bumper.ConfServer(conf_address_8007, usessl=False,bumper_users=bumper.bumper_users_var, bumper_bots=bumper.bumper_bots_var,bumper_clients=bumper.bumper_clients_var, helperbot=mqtt_helperbot) - - #add user - # users = bumper.bumper_users_var.get() + + xmpp_server = bumper.XMPPServer( + xmpp_address, + bumper_users=bumper.bumper_users_var, + bumper_bots=bumper.bumper_bots_var, + bumper_clients=bumper.bumper_clients_var, + ) + mqtt_server = bumper.MQTTServer( + mqtt_address, + bumper_users=bumper.bumper_users_var, + bumper_bots=bumper.bumper_bots_var, + bumper_clients=bumper.bumper_clients_var, + ) + mqtt_helperbot = bumper.MQTTHelperBot( + mqtt_address, + bumper_bots=bumper.bumper_bots_var, + bumper_clients=bumper.bumper_clients_var, + ) + conf_server = bumper.ConfServer( + conf_address_443, + usessl=True, + bumper_users=bumper.bumper_users_var, + bumper_bots=bumper.bumper_bots_var, + bumper_clients=bumper.bumper_clients_var, + helperbot=mqtt_helperbot, + ) + conf_server_2 = bumper.ConfServer( + conf_address_8007, + usessl=False, + bumper_users=bumper.bumper_users_var, + bumper_bots=bumper.bumper_bots_var, + bumper_clients=bumper.bumper_clients_var, + helperbot=mqtt_helperbot, + ) + + # add user + # users = bumper.bumper_users_var.get() # user1 = bumper.BumperUser('user1') # user1.add_device('devid') - # user1.add_bot('bot_did') - # users.append(user1) + # user1.add_bot('bot_did') + # users.append(user1) # bumper.bumper_users_var.set(users) # start xmpp server on port 5223 (sync) - xmpp_server.run(run_async=True) #Start in new thread + xmpp_server.run(run_async=True) # Start in new thread - # start mqtt server on port 8883 (async) - mqtt_server.run(run_async=True) #Start in new thread - - time.sleep(1.5) #Wait for broker startup + # start mqtt server on port 8883 (async) + mqtt_server.run(run_async=True) # Start in new thread - # start mqtt_helperbot (async) - mqtt_helperbot.run(run_async=True) #Start in new thread + time.sleep(1.5) # Wait for broker startup - # start conf server on port 443 (async) - Used for most https calls - conf_server.run(run_async=True) #Start in new thread + # start mqtt_helperbot (async) + mqtt_helperbot.run(run_async=True) # Start in new thread - # start conf server on port 8007 (async) - Used for a load balancer request - conf_server_2.run(run_async=True) #Start in new thread + # start conf server on port 443 (async) - Used for most https calls + conf_server.run(run_async=True) # Start in new thread + + # start conf server on port 8007 (async) - Used for a load balancer request + conf_server_2.run(run_async=True) # Start in new thread while True: try: @@ -72,13 +104,14 @@ def main(): # if uid != "": # xmpp_server.remove_client_byuid(uid) #Remove clients from xmpp server # remove_clients.remove(uid) - + # bumper.bumper_removeclients_var.set(remove_clients) - - except KeyboardInterrupt: + + except KeyboardInterrupt: bumper.bumperlog.info("Bumper Exiting - Keyboard Interrupt") print("Bumper Exiting") exit(1) - + + if __name__ == "__main__": - main() \ No newline at end of file + main() diff --git a/bumper/__init__.py b/bumper/__init__.py index 5e260f2..77e54be 100644 --- a/bumper/__init__.py +++ b/bumper/__init__.py @@ -10,39 +10,40 @@ import time import logging from base64 import b64decode, b64encode -bumper_users_var = contextvars.ContextVar('bumper_users', default=[]) -bumper_clients_var = contextvars.ContextVar('bumper_clients', default=[]) -bumper_bots_var = contextvars.ContextVar('bumper_bots', default=[]) +bumper_users_var = contextvars.ContextVar("bumper_users", default=[]) +bumper_clients_var = contextvars.ContextVar("bumper_clients", default=[]) +bumper_bots_var = contextvars.ContextVar("bumper_bots", default=[]) -ca_cert = './certs/CA/cacert.pem' -server_cert = './certs/cert.pem' -server_key = './certs/key.pem' +ca_cert = "./certs/CA/cacert.pem" +server_cert = "./certs/cert.pem" +server_key = "./certs/key.pem" use_auth = False -#Logs +# Logs bumperlog = logging.getLogger("bumper") confserverlog = logging.getLogger("confserver") -#Override the logging level -#confserverlog.setLevel(logging.INFO) +# Override the logging level +# confserverlog.setLevel(logging.INFO) mqttserverlog = logging.getLogger("mqttserver") -#Override the logging level -#mqttserverlog.setLevel(logging.INFO) +# Override the logging level +# mqttserverlog.setLevel(logging.INFO) helperbotlog = logging.getLogger("helperbot") -#Override the logging level -#helperbotlog.setLevel(logging.INFO) +# Override the logging level +# helperbotlog.setLevel(logging.INFO) xmppserverlog = logging.getLogger("xmppserver") -#Override the logging level -#xmppserverlog.setLevel(logging.INFO) +# Override the logging level +# xmppserverlog.setLevel(logging.INFO) + def get_milli_time(timetoconvert): return int(round(timetoconvert * 1000)) class BumperUser(object): - def __init__(self,userid=""): + def __init__(self, userid=""): self.userid = userid - self.devices = [] + self.devices = [] self.tokens = [] self.authcodes = [] self.bots = [] @@ -55,7 +56,6 @@ class BumperUser(object): if devid in self.devices: self.devices.remove(devid) - def add_token(self, token): if not token in self.tokens: self.tokens.append(token) @@ -80,8 +80,17 @@ class BumperUser(object): if botdid in self.bots: self.bots.remove(botdid) + class VacBotDevice(object): - def __init__(self,did="", vac_bot_device_class="",resource="" , name="", nick="", company="eco-ng"): + def __init__( + self, + did="", + vac_bot_device_class="", + resource="", + name="", + nick="", + company="eco-ng", + ): self.vac_bot_device_class = vac_bot_device_class self.company = company self.did = did @@ -92,11 +101,18 @@ class VacBotDevice(object): self.xmpp_connection = False def asdict(self): - return {"class": self.vac_bot_device_class, "company": self.company, - "did": self.did, "name": self.name, "nick": self.nick, "resource": self.resource} + return { + "class": self.vac_bot_device_class, + "company": self.company, + "did": self.did, + "name": self.name, + "nick": self.nick, + "resource": self.resource, + } + class VacBotClient(object): - def __init__(self,userid="",realm="",token=""): + def __init__(self, userid="", realm="", token=""): self.userid = userid self.realm = realm self.resource = token @@ -104,23 +120,25 @@ class VacBotClient(object): self.xmpp_connection = False def asdict(self): - return {"userid": self.userid,"realm": self.realm,"resource": self.resource} + return {"userid": self.userid, "realm": self.realm, "resource": self.resource} + def check_authcode(uid, authcode): users = bumper_users_var.get() for user in users: - if uid == "fuid_{}".format(user.userid) and authcode in user.authcodes: + if uid == "fuid_{}".format(user.userid) and authcode in user.authcodes: return True - return False + return False + def add_bot(sn, did, devclass, resource): - + newbot = VacBotDevice() - newbot.did = did + newbot.did = did newbot.name = sn newbot.vac_bot_device_class = devclass - newbot.resource = resource + newbot.resource = resource bots = bumper_bots_var.get() existingbot = False @@ -128,26 +146,27 @@ def add_bot(sn, did, devclass, resource): if bot.did == newbot.did: existingbot = True - if existingbot == False: + if existingbot == False: bots.append(newbot) bumperlog.info("new bot added SN: {} DID: {}".format(newbot.name, newbot.did)) bumper_bots_var.set(bots) + def add_client(userid, realm, resource): - + newclient = VacBotClient() newclient.userid = userid newclient.realm = realm - newclient.resource = resource + newclient.resource = resource + + clients = bumper_clients_var.get() - clients = bumper_clients_var.get() - existingclient = False for client in clients: if client.userid == newclient.userid: existingclient = True - if existingclient == False: + if existingclient == False: clients.append(newclient) bumperlog.info("new client added {}".format(newclient.userid)) bumper_clients_var.set(clients) diff --git a/bumper/confserver.py b/bumper/confserver.py index c3f76ba..70c37d8 100644 --- a/bumper/confserver.py +++ b/bumper/confserver.py @@ -12,45 +12,62 @@ import contextvars from aiohttp import web import uuid + class aiohttp_filter(logging.Filter): - def filter(self, record): - if record.name == "aiohttp.access" and record.levelno == 20: #Filters aiohttp.access log to switch it from INFO to DEBUG + if ( + record.name == "aiohttp.access" and record.levelno == 20 + ): # Filters aiohttp.access log to switch it from INFO to DEBUG record.levelno = 10 - record.levelname = "DEBUG" - - if record.levelno == 10 and logging.getLogger("confserver").getEffectiveLevel() == 10: + record.levelname = "DEBUG" + + if ( + record.levelno == 10 + and logging.getLogger("confserver").getEffectiveLevel() == 10 + ): return True else: return False + confserverlog = logging.getLogger("confserver") -logging.getLogger("asyncio").setLevel(logging.CRITICAL + 1) #Ignore this logger +logging.getLogger("asyncio").setLevel(logging.CRITICAL + 1) # Ignore this logger logging.getLogger("aiohttp.access").addFilter(aiohttp_filter()) -class ConfServer(): + +class ConfServer: bumper_clients = contextvars.ContextVar bumper_bots = contextvars.ContextVar - def __init__(self, address, usessl=False, bumper_users=contextvars.ContextVar, bumper_bots=contextvars.ContextVar, bumper_clients=contextvars.ContextVar, helperbot=None): + def __init__( + self, + address, + usessl=False, + bumper_users=contextvars.ContextVar, + bumper_bots=contextvars.ContextVar, + bumper_clients=contextvars.ContextVar, + helperbot=None, + ): self.bumper_users = bumper_users self.bumper_bots = bumper_bots - self.bumper_clients = bumper_clients + self.bumper_clients = bumper_clients self.helperbot = helperbot self.usessl = usessl - self.address = address + self.address = address self.confthread = None - def run(self, run_async=False): try: if run_async: confserverlog.debug("Starting ConfServer Thread: 1") - self.confthread = Thread(name="ConfServer_{}_Thread".format(self.address[1]),target=self.run_server) + self.confthread = Thread( + name="ConfServer_{}_Thread".format(self.address[1]), + target=self.run_server, + ) self.confthread.setDaemon(True) self.confthread.start() - + else: try: self.run_server() @@ -58,8 +75,7 @@ class ConfServer(): self.disconnect() except Exception as e: - confserverlog.exception('{}'.format(e)) - + confserverlog.exception("{}".format(e)) def run_server(self): logging.info("Starting ConfServer at {}".format(self.address)) @@ -68,433 +84,577 @@ class ConfServer(): loop = asyncio.get_event_loop() except: loop = asyncio.new_event_loop() - + try: loop.run_until_complete(self.start_server()) loop.run_forever() except Exception as e: - confserverlog.exception('{}'.format(e)) - + confserverlog.exception("{}".format(e)) - async def start_server(self): + async def start_server(self): try: - app = web.Application() + app = web.Application() - app.add_routes([ - web.get('', self.handle_base), - web.get('/{apiversion}/private/{country}/{language}/{devid}/{apptype}/{appversion}/{devtype}/{aid}/user/login', self.handle_login), - web.get('/{apiversion}/private/{country}/{language}/{devid}/{apptype}/{appversion}/{devtype}/{aid}/user/checkLogin', self.handle_login), - web.get('/{apiversion}/private/{country}/{language}/{devid}/{apptype}/{appversion}/{devtype}/{aid}/user/logout', self.handle_logout), - web.get('/{apiversion}/private/{country}/{language}/{devid}/{apptype}/{appversion}/{devtype}/{aid}/user/getAuthCode', self.handle_getAuthCode), - web.get('/{apiversion}/private/{country}/{language}/{devid}/{apptype}/{appversion}/{devtype}/{aid}/user/checkAgreement', self.handle_checkAgreement), - web.get('/{apiversion}/private/{country}/{language}/{devid}/{apptype}/{appversion}/{devtype}/{aid}/common/checkVersion', self.handle_checkVersion), - web.get('/{apiversion}/private/{country}/{language}/{devid}/{apptype}/{appversion}/{devtype}/{aid}/campaign/homePageAlert', self.handle_homePageAlert), - - web.post('/api/users/user.do', self.handle_usersapi), - web.get('/api/users/user.do', self.handle_usersapi), - web.post('/api/pim/product/getProductIotMap', self.handle_getProductIotMap), - web.post('/api/iot/devmanager.do', self.handle_devmanager_botcommand), - - web.post('/lookup.do', self.handle_lookup), - ]) - #Direct register from app: - #/{apiversion}/private/{country}/{language}/{devid}/{apptype}/{appversion}/{devtype}/{aid}/user/directRegister + app.add_routes( + [ + web.get("", self.handle_base), + web.get( + "/{apiversion}/private/{country}/{language}/{devid}/{apptype}/{appversion}/{devtype}/{aid}/user/login", + self.handle_login, + ), + web.get( + "/{apiversion}/private/{country}/{language}/{devid}/{apptype}/{appversion}/{devtype}/{aid}/user/checkLogin", + self.handle_login, + ), + web.get( + "/{apiversion}/private/{country}/{language}/{devid}/{apptype}/{appversion}/{devtype}/{aid}/user/logout", + self.handle_logout, + ), + web.get( + "/{apiversion}/private/{country}/{language}/{devid}/{apptype}/{appversion}/{devtype}/{aid}/user/getAuthCode", + self.handle_getAuthCode, + ), + web.get( + "/{apiversion}/private/{country}/{language}/{devid}/{apptype}/{appversion}/{devtype}/{aid}/user/checkAgreement", + self.handle_checkAgreement, + ), + web.get( + "/{apiversion}/private/{country}/{language}/{devid}/{apptype}/{appversion}/{devtype}/{aid}/common/checkVersion", + self.handle_checkVersion, + ), + web.get( + "/{apiversion}/private/{country}/{language}/{devid}/{apptype}/{appversion}/{devtype}/{aid}/campaign/homePageAlert", + self.handle_homePageAlert, + ), + web.post("/api/users/user.do", self.handle_usersapi), + web.get("/api/users/user.do", self.handle_usersapi), + web.post( + "/api/pim/product/getProductIotMap", + self.handle_getProductIotMap, + ), + web.post( + "/api/iot/devmanager.do", self.handle_devmanager_botcommand + ), + web.post("/lookup.do", self.handle_lookup), + ] + ) + # Direct register from app: + # /{apiversion}/private/{country}/{language}/{devid}/{apptype}/{appversion}/{devtype}/{aid}/user/directRegister runner = web.AppRunner(app) await runner.setup() if self.usessl: ssl_ctx = ssl.create_default_context(ssl.Purpose.CLIENT_AUTH) - ssl_ctx.load_cert_chain(bumper.server_cert,bumper.server_key) - site = web.TCPSite(runner, host=self.address[0], port=self.address[1],ssl_context=ssl_ctx) - - else: + ssl_ctx.load_cert_chain(bumper.server_cert, bumper.server_key) + site = web.TCPSite( + runner, + host=self.address[0], + port=self.address[1], + ssl_context=ssl_ctx, + ) + + else: site = web.TCPSite(runner, host=self.address[0], port=self.address[1]) - + await site.start() - + except PermissionError as e: if "bind" in e.strerror: - confserverlog.exception("Error binding confserver, exiting. Try using a different hostname or IP - {}".format(e)) + confserverlog.exception( + "Error binding confserver, exiting. Try using a different hostname or IP - {}".format( + e + ) + ) exit(1) except Exception as e: - confserverlog.exception('{}'.format(e)) + confserverlog.exception("{}".format(e)) exit(1) - async def handle_base(self, request): + async def handle_base(self, request): try: text = "Bumper!" return web.json_response(text) - - except Exception as e: - confserverlog.exception('{}'.format(e)) - async def handle_login(self, request): + except Exception as e: + confserverlog.exception("{}".format(e)) + + async def handle_login(self, request): try: - user_devid = request.match_info.get('devid', "") - countrycode = request.match_info.get('country', "us") - confserverlog.info('client with devid {} attempting login'.format(user_devid)) + user_devid = request.match_info.get("devid", "") + countrycode = request.match_info.get("country", "us") + confserverlog.info( + "client with devid {} attempting login".format(user_devid) + ) if bumper.use_auth: - if not user_devid == "": #Performing basic "auth" using devid, super insecure - users = self.bumper_users.get() + if ( + not user_devid == "" + ): # Performing basic "auth" using devid, super insecure + users = self.bumper_users.get() for user in users: if user_devid in user.devices: - tmpaccesstoken = '' - if 'checkLogin' in request.path: - if request.query['accessToken'] in user.tokens and request.query['uid'] == "fuid_{}".format(user.userid): - tmpaccesstoken = request.query['accessToken'] + tmpaccesstoken = "" + if "checkLogin" in request.path: + if request.query[ + "accessToken" + ] in user.tokens and request.query[ + "uid" + ] == "fuid_{}".format( + user.userid + ): + tmpaccesstoken = request.query["accessToken"] body = { "code": bumper.RETURN_API_SUCCESS, "data": { - "accessToken": tmpaccesstoken, #Random chars 32 length - "country": countrycode, - "email": "null@null.com", - "uid": "fuid_{}".format(user.userid), - "username": "fusername_{}".format(user.userid), + "accessToken": tmpaccesstoken, # Random chars 32 length + "country": countrycode, + "email": "null@null.com", + "uid": "fuid_{}".format(user.userid), + "username": "fusername_{}".format( + user.userid + ), }, "msg": "操作成功", - "time": bumper.get_milli_time(time.time()) - } + "time": bumper.get_milli_time(time.time()), + } else: body = { - "code": bumper.ERR_TOKEN_INVALID, - "data": None, - "msg": "当前密码错误", - "time": bumper.get_milli_time(time.time()) - } + "code": bumper.ERR_TOKEN_INVALID, + "data": None, + "msg": "当前密码错误", + "time": bumper.get_milli_time(time.time()), + } else: - if tmpaccesstoken == '': + if tmpaccesstoken == "": tmpaccesstoken = uuid.uuid4().hex - user.add_token(tmpaccesstoken) - + user.add_token(tmpaccesstoken) + body = { - "code": bumper.RETURN_API_SUCCESS, - "data": { - "accessToken": tmpaccesstoken, #Random chars 32 length + "code": bumper.RETURN_API_SUCCESS, + "data": { + "accessToken": tmpaccesstoken, # Random chars 32 length "country": countrycode, "email": "null@null.com", "uid": "fuid_{}".format(user.userid), "username": "fusername_{}".format(user.userid), - }, - "msg": "操作成功", - "time": bumper.get_milli_time(time.time()) - } + }, + "msg": "操作成功", + "time": bumper.get_milli_time(time.time()), + } self.bumper_users.set(users) - + return web.json_response(body) - + body = { - "code": bumper.ERR_USER_NOT_ACTIVATED, - "data": None, - "msg": "当前密码错误", - "time": bumper.get_milli_time(time.time()) - } + "code": bumper.ERR_USER_NOT_ACTIVATED, + "data": None, + "msg": "当前密码错误", + "time": bumper.get_milli_time(time.time()), + } return web.json_response(body) - else: - return web.json_response(self._auth_any(user_devid, countrycode, request)) - - except Exception as e: - confserverlog.exception('{}'.format(e)) + else: + return web.json_response( + self._auth_any(user_devid, countrycode, request) + ) + except Exception as e: + confserverlog.exception("{}".format(e)) def _auth_any(self, devid, country, request): try: user_devid = devid countrycode = country - tmpaccesstoken = '' + tmpaccesstoken = "" users = self.bumper_users.get() bots = self.bumper_bots.get() if len(users) > 0: tmpuser = users[0] tmpuser.add_device(user_devid) - else: - tmpuser = bumper.BumperUser('tmpuser') + else: + tmpuser = bumper.BumperUser("tmpuser") users.append(tmpuser) tmpuser.add_device(user_devid) for bot in bots: tmpuser.add_bot(bot.did) - if 'checkLogin' in request.path: - tmpaccesstoken = request.query['accessToken'] - tmpuser.add_token(tmpaccesstoken) + if "checkLogin" in request.path: + tmpaccesstoken = request.query["accessToken"] + tmpuser.add_token(tmpaccesstoken) body = { "code": bumper.RETURN_API_SUCCESS, "data": { - "accessToken": tmpaccesstoken, #Random chars 32 length - "country": countrycode, - "email": "null@null.com", - "uid": "fuid_{}".format(tmpuser.userid), - "username": "fusername_{}".format(tmpuser.userid), - }, - "msg": "操作成功", - "time": bumper.get_milli_time(time.time()) - } - else: - if tmpaccesstoken == '': - tmpaccesstoken = uuid.uuid4().hex - tmpuser.add_token(tmpaccesstoken) - - body = { - "code": bumper.RETURN_API_SUCCESS, - "data": { - "accessToken": tmpaccesstoken, #Random chars 32 length + "accessToken": tmpaccesstoken, # Random chars 32 length "country": countrycode, "email": "null@null.com", "uid": "fuid_{}".format(tmpuser.userid), "username": "fusername_{}".format(tmpuser.userid), - }, - "msg": "操作成功", - "time": bumper.get_milli_time(time.time()) - } + }, + "msg": "操作成功", + "time": bumper.get_milli_time(time.time()), + } + else: + if tmpaccesstoken == "": + tmpaccesstoken = uuid.uuid4().hex + tmpuser.add_token(tmpaccesstoken) + + body = { + "code": bumper.RETURN_API_SUCCESS, + "data": { + "accessToken": tmpaccesstoken, # Random chars 32 length + "country": countrycode, + "email": "null@null.com", + "uid": "fuid_{}".format(tmpuser.userid), + "username": "fusername_{}".format(tmpuser.userid), + }, + "msg": "操作成功", + "time": bumper.get_milli_time(time.time()), + } self.bumper_users.set(users) return body except Exception as e: - confserverlog.exception('{}'.format(e)) + confserverlog.exception("{}".format(e)) - async def handle_logout(self, request): + async def handle_logout(self, request): try: - user_devid = request.match_info.get('devid', "") + user_devid = request.match_info.get("devid", "") if not user_devid == "": users = self.bumper_users.get() for user in users: if user_devid in user.devices: - if request.query['uid'] == "fuid_{}".format(user.userid) and request.query['accessToken'] in user.tokens: - user.revoke_token(request.query['accessToken']) + if ( + request.query["uid"] == "fuid_{}".format(user.userid) + and request.query["accessToken"] in user.tokens + ): + user.revoke_token(request.query["accessToken"]) self.bumper_users.set(users) - body = {"code": bumper.RETURN_API_SUCCESS,"data": None,"msg": "操作成功", "time": bumper.get_milli_time(time.time())} + body = { + "code": bumper.RETURN_API_SUCCESS, + "data": None, + "msg": "操作成功", + "time": bumper.get_milli_time(time.time()), + } - return web.json_response(body) + return web.json_response(body) except Exception as e: - confserverlog.exception('{}'.format(e)) + confserverlog.exception("{}".format(e)) - async def handle_getAuthCode(self, request): + async def handle_getAuthCode(self, request): try: - user_devid = request.match_info.get('devid', "") + user_devid = request.match_info.get("devid", "") if not user_devid == "": users = self.bumper_users.get() if len(users) > 0: for user in users: - if user_devid in user.devices and request.query['accessToken'] in user.tokens: - countrycode = request.match_info.get('country', "us") - tmpauthcode = "{}_{}".format(countrycode,uuid.uuid4().hex) - user.add_authcode(tmpauthcode) - + if ( + user_devid in user.devices + and request.query["accessToken"] in user.tokens + ): + countrycode = request.match_info.get("country", "us") + tmpauthcode = "{}_{}".format(countrycode, uuid.uuid4().hex) + user.add_authcode(tmpauthcode) + body = { - "code": bumper.RETURN_API_SUCCESS, - "data": { + "code": bumper.RETURN_API_SUCCESS, + "data": { "authCode": tmpauthcode, - "ecovacsUid": request.query['uid'] - }, - "msg": "操作成功", - "time": bumper.get_milli_time(time.time()) - } + "ecovacsUid": request.query["uid"], + }, + "msg": "操作成功", + "time": bumper.get_milli_time(time.time()), + } self.bumper_users.set(users) - return web.json_response(body) - + return web.json_response(body) + body = { - "code": bumper.ERR_TOKEN_INVALID, - "data": None, - "msg": "当前密码错误", - "time": bumper.get_milli_time(time.time()) - } - + "code": bumper.ERR_TOKEN_INVALID, + "data": None, + "msg": "当前密码错误", + "time": bumper.get_milli_time(time.time()), + } + return web.json_response(body) except Exception as e: - confserverlog.exception('{}'.format(e)) + confserverlog.exception("{}".format(e)) - async def handle_checkVersion(self, request): + async def handle_checkVersion(self, request): try: body = { - "code": bumper.RETURN_API_SUCCESS, - "data": { - "c": None, - "img": None, - "r": 0, - "t": None, - "u": None, - "ut": 0, - "v": None - }, - "msg": "操作成功", - "time": bumper.get_milli_time(time.time()) - } - + "code": bumper.RETURN_API_SUCCESS, + "data": { + "c": None, + "img": None, + "r": 0, + "t": None, + "u": None, + "ut": 0, + "v": None, + }, + "msg": "操作成功", + "time": bumper.get_milli_time(time.time()), + } + return web.json_response(body) except Exception as e: - confserverlog.exception('{}'.format(e)) + confserverlog.exception("{}".format(e)) - async def handle_checkAgreement(self, request): + async def handle_checkAgreement(self, request): try: body = { - "code": bumper.RETURN_API_SUCCESS, - "data": [], - "msg": "操作成功", - "time": bumper.get_milli_time(time.time()) - - } - - return web.json_response(body) + "code": bumper.RETURN_API_SUCCESS, + "data": [], + "msg": "操作成功", + "time": bumper.get_milli_time(time.time()), + } + + return web.json_response(body) except Exception as e: - confserverlog.exception('{}'.format(e)) + confserverlog.exception("{}".format(e)) - async def handle_homePageAlert(self, request): + async def handle_homePageAlert(self, request): try: - nextAlert = bumper.get_milli_time((datetime.now() + timedelta(hours=12)).timestamp()) + nextAlert = bumper.get_milli_time( + (datetime.now() + timedelta(hours=12)).timestamp() + ) body = { - "code": bumper.RETURN_API_SUCCESS, - "data": { - "clickSchemeUrl": None, - "clickWebUrl": None, - "hasCampaign": "N", - "imageUrl": None, - "nextAlertTime": nextAlert, - "serverTime": bumper.get_milli_time(time.time()) + "code": bumper.RETURN_API_SUCCESS, + "data": { + "clickSchemeUrl": None, + "clickWebUrl": None, + "hasCampaign": "N", + "imageUrl": None, + "nextAlertTime": nextAlert, + "serverTime": bumper.get_milli_time(time.time()), + }, + "msg": "操作成功", + "time": bumper.get_milli_time(time.time()), + } + + return web.json_response(body) + + except Exception as e: + confserverlog.exception("{}".format(e)) + + async def handle_getProductIotMap(self, request): + try: + body = { + "code": bumper.RETURN_API_SUCCESS, + "data": [ + { + "classid": "dl8fht", + "product": { + "_id": "5acb0fa87c295c0001876ecf", + "name": "DEEBOT 600 Series", + "icon": "5acc32067c295c0001876eea", + "UILogicId": "dl8fht", + "ota": False, + "iconUrl": "https://portal-ww.ecouser.net/api/pim/file/get/5acc32067c295c0001876eea", + }, }, - "msg": "操作成功", - "time": bumper.get_milli_time(time.time()) - } - - return web.json_response(body) + { + "classid": "02uwxm", + "product": { + "_id": "5ae1481e7ccd1a0001e1f69e", + "name": "DEEBOT OZMO Slim10 Series", + "icon": "5b1dddc48bc45700014035a1", + "UILogicId": "02uwxm", + "ota": False, + "iconUrl": "https://portal-ww.ecouser.net/api/pim/file/get/5b1dddc48bc45700014035a1", + }, + }, + { + "classid": "y79a7u", + "product": { + "_id": "5b04c0227ccd1a0001e1f6a8", + "name": "DEEBOT OZMO 900", + "icon": "5b04c0217ccd1a0001e1f6a7", + "UILogicId": "y79a7u", + "ota": True, + "iconUrl": "https://portal-ww.ecouser.net/api/pim/file/get/5b04c0217ccd1a0001e1f6a7", + }, + }, + { + "classid": "jr3pqa", + "product": { + "_id": "5b43077b8bc457000140363e", + "name": "DEEBOT 711", + "icon": "5b5ac4cc8d5a56000111e769", + "UILogicId": "jr3pqa", + "ota": True, + "iconUrl": "https://portal-ww.ecouser.net/api/pim/file/get/5b5ac4cc8d5a56000111e769", + }, + }, + { + "classid": "uv242z", + "product": { + "_id": "5b5149b4ac0b87000148c128", + "name": "DEEBOT 710", + "icon": "5b5ac4e45f21100001882bb9", + "UILogicId": "uv242z", + "ota": True, + "iconUrl": "https://portal-ww.ecouser.net/api/pim/file/get/5b5ac4e45f21100001882bb9", + }, + }, + { + "classid": "ls1ok3", + "product": { + "_id": "5b6561060506b100015c8868", + "name": "DEEBOT 900 Series", + "icon": "5ba4a2cb6c2f120001c32839", + "UILogicId": "ls1ok3", + "ota": True, + "iconUrl": "https://portal-ww.ecouser.net/api/pim/file/get/5ba4a2cb6c2f120001c32839", + }, + }, + ], + } + return web.json_response(body) except Exception as e: - confserverlog.exception('{}'.format(e)) + confserverlog.exception("{}".format(e)) - async def handle_getProductIotMap(self, request): - try: - body = {"code":bumper.RETURN_API_SUCCESS,"data":[{"classid":"dl8fht","product":{"_id":"5acb0fa87c295c0001876ecf","name":"DEEBOT 600 Series","icon":"5acc32067c295c0001876eea","UILogicId":"dl8fht","ota":False,"iconUrl":"https://portal-ww.ecouser.net/api/pim/file/get/5acc32067c295c0001876eea"}},{"classid":"02uwxm","product":{"_id":"5ae1481e7ccd1a0001e1f69e","name":"DEEBOT OZMO Slim10 Series","icon":"5b1dddc48bc45700014035a1","UILogicId":"02uwxm","ota":False,"iconUrl":"https://portal-ww.ecouser.net/api/pim/file/get/5b1dddc48bc45700014035a1"}},{"classid":"y79a7u","product":{"_id":"5b04c0227ccd1a0001e1f6a8","name":"DEEBOT OZMO 900","icon":"5b04c0217ccd1a0001e1f6a7","UILogicId":"y79a7u","ota":True,"iconUrl":"https://portal-ww.ecouser.net/api/pim/file/get/5b04c0217ccd1a0001e1f6a7"}},{"classid":"jr3pqa","product":{"_id":"5b43077b8bc457000140363e","name":"DEEBOT 711","icon":"5b5ac4cc8d5a56000111e769","UILogicId":"jr3pqa","ota":True,"iconUrl":"https://portal-ww.ecouser.net/api/pim/file/get/5b5ac4cc8d5a56000111e769"}},{"classid":"uv242z","product":{"_id":"5b5149b4ac0b87000148c128","name":"DEEBOT 710","icon":"5b5ac4e45f21100001882bb9","UILogicId":"uv242z","ota":True,"iconUrl":"https://portal-ww.ecouser.net/api/pim/file/get/5b5ac4e45f21100001882bb9"}},{"classid":"ls1ok3","product":{"_id":"5b6561060506b100015c8868","name":"DEEBOT 900 Series","icon":"5ba4a2cb6c2f120001c32839","UILogicId":"ls1ok3","ota":True,"iconUrl":"https://portal-ww.ecouser.net/api/pim/file/get/5ba4a2cb6c2f120001c32839"}}]} - return web.json_response(body) - - except Exception as e: - confserverlog.exception('{}'.format(e)) - - async def handle_usersapi(self, request): + async def handle_usersapi(self, request): try: body = {} postbody = {} if request.content_type == "application/x-www-form-urlencoded": postbody = await request.post() - + else: - postbody = json.loads(await request.text()) - - todo = postbody['todo'] - if todo == 'FindBest': - service = postbody['service'] - if service == 'EcoMsgNew': - body = {"result":"ok","ip":socket.gethostbyname(socket.gethostname()),"port":5223} - elif service == 'EcoUpdate': - body = {"result":"ok","ip":"47.88.66.164","port":8005} - elif todo == 'loginByItToken': - + postbody = json.loads(await request.text()) + + todo = postbody["todo"] + if todo == "FindBest": + service = postbody["service"] + if service == "EcoMsgNew": + body = { + "result": "ok", + "ip": socket.gethostbyname(socket.gethostname()), + "port": 5223, + } + elif service == "EcoUpdate": + body = {"result": "ok", "ip": "47.88.66.164", "port": 8005} + elif todo == "loginByItToken": + users = self.bumper_users.get() for user in users: - if postbody['userId'] == "fuid_{}".format(user.userid) and postbody['token'] in user.authcodes: + if ( + postbody["userId"] == "fuid_{}".format(user.userid) + and postbody["token"] in user.authcodes + ): body = { "resource": postbody["resource"], "result": "ok", "todo": "result", - "token": postbody["token"], - "userId": postbody["userId"] - } + "token": postbody["token"], + "userId": postbody["userId"], + } - elif todo == 'GetDeviceList': + elif todo == "GetDeviceList": active_bots = self.bumper_bots.get() bot_list = [] for bot in active_bots: bot_list.append(bot.asdict()) - body = { - "devices": bot_list, - "result": "ok", - "todo": "result" - } + body = {"devices": bot_list, "result": "ok", "todo": "result"} - elif todo == 'SetDeviceNick': + elif todo == "SetDeviceNick": bots = self.bumper_bots.get() for bot in bots: - if postbody['did'] == bot.did: - bot.nick = postbody['nick'] + if postbody["did"] == bot.did: + bot.nick = postbody["nick"] self.bumper_bots.set(bots) - body = { - "result": "ok", - "todo": "result", - } + body = {"result": "ok", "todo": "result"} - confserverlog.debug("\r\n POST: {} \r\n Response: {}".format(postbody,body)) - return web.json_response(body) + confserverlog.debug( + "\r\n POST: {} \r\n Response: {}".format(postbody, body) + ) + return web.json_response(body) except Exception as e: - confserverlog.exception('{}'.format(e)) + confserverlog.exception("{}".format(e)) - async def handle_lookup(self, request): + async def handle_lookup(self, request): try: body = {} postbody = {} if request.content_type == "application/x-www-form-urlencoded": postbody = await request.post() - + else: postbody = json.loads(await request.text()) - + confserverlog.debug(postbody) - todo = postbody['todo'] - if todo == 'FindBest': - service = postbody['service'] - if service == 'EcoMsgNew': - body = {"result":"ok","ip":socket.gethostbyname(socket.gethostname()),"port":5223} - elif service == 'EcoUpdate': - body = {"result":"ok","ip":"47.88.66.164","port":8005} - - confserverlog.debug("\r\n POST: {} \r\n Response: {}".format(postbody,body)) - return web.json_response(body) + todo = postbody["todo"] + if todo == "FindBest": + service = postbody["service"] + if service == "EcoMsgNew": + body = { + "result": "ok", + "ip": socket.gethostbyname(socket.gethostname()), + "port": 5223, + } + elif service == "EcoUpdate": + body = {"result": "ok", "ip": "47.88.66.164", "port": 8005} + + confserverlog.debug( + "\r\n POST: {} \r\n Response: {}".format(postbody, body) + ) + return web.json_response(body) except Exception as e: - confserverlog.exception('{}'.format(e)) + confserverlog.exception("{}".format(e)) async def handle_devmanager_botcommand(self, request): try: json_body = json.loads(await request.text()) - randomid = ''.join(random.sample(string.ascii_letters,6)) + randomid = "".join(random.sample(string.ascii_letters, 6)) bots = self.bumper_bots.get() for bot in bots: - if bot.did == json_body['toId'] and bot.mqtt_connection == True: + if bot.did == json_body["toId"] and bot.mqtt_connection == True: retcmd = await self.helperbot.send_command(json_body, randomid) body = retcmd - confserverlog.debug("\r\n POST: {} \r\n Response: {}".format(json_body,body)) - return web.json_response(body) + confserverlog.debug( + "\r\n POST: {} \r\n Response: {}".format(json_body, body) + ) + return web.json_response(body) else: - confserverlog.error("No bots with DID: {} connected to MQTT".format(json_body['toId'])) - body = { "id": randomid, "errno": bumper.ERR_COMMON, "ret": "fail" } - return web.json_response(body) + confserverlog.error( + "No bots with DID: {} connected to MQTT".format( + json_body["toId"] + ) + ) + body = {"id": randomid, "errno": bumper.ERR_COMMON, "ret": "fail"} + return web.json_response(body) except Exception as e: - confserverlog.exception('{}'.format(e)) + confserverlog.exception("{}".format(e)) def disconnect(self): try: - confserverlog.info('shutting down') - if(self.run_async): + confserverlog.info("shutting down") + if self.run_async: self.confthread.join() else: - self.confthread.disconnect() - + self.confthread.disconnect() + except Exception as e: - confserverlog.exception('{}'.format(e)) \ No newline at end of file + confserverlog.exception("{}".format(e)) + diff --git a/bumper/mqttserver.py b/bumper/mqttserver.py index 11bd4d0..35285b2 100644 --- a/bumper/mqttserver.py +++ b/bumper/mqttserver.py @@ -19,282 +19,335 @@ from datetime import datetime, timedelta helperbotlog = logging.getLogger("helperbot") mqttserverlog = logging.getLogger("mqttserver") -logging.getLogger("transitions").setLevel(logging.CRITICAL + 1) #Ignore this logger -logging.getLogger("passlib").setLevel(logging.CRITICAL + 1) #Ignore this logger -logging.getLogger("hbmqtt.broker").setLevel(logging.CRITICAL + 1) #Ignore this logger #There are some sublogs that could be set if needed (.plugins) -logging.getLogger("hbmqtt.mqtt.protocol").setLevel(logging.CRITICAL + 1) #Ignore this logger -logging.getLogger("hbmqtt.client").setLevel(logging.CRITICAL + 1) #Ignore this logger +logging.getLogger("transitions").setLevel(logging.CRITICAL + 1) # Ignore this logger +logging.getLogger("passlib").setLevel(logging.CRITICAL + 1) # Ignore this logger +logging.getLogger("hbmqtt.broker").setLevel( + logging.CRITICAL + 1 +) # Ignore this logger #There are some sublogs that could be set if needed (.plugins) +logging.getLogger("hbmqtt.mqtt.protocol").setLevel( + logging.CRITICAL + 1 +) # Ignore this logger +logging.getLogger("hbmqtt.client").setLevel(logging.CRITICAL + 1) # Ignore this logger + + +class MQTTHelperBot: -class MQTTHelperBot(): - Client = MQTTClient() - def __init__(self, address, bumper_bots=contextvars.ContextVar, bumper_clients=contextvars.ContextVar): + + def __init__( + self, + address, + bumper_bots=contextvars.ContextVar, + bumper_clients=contextvars.ContextVar, + ): self.address = address - self.client_id = "helper1@bumper/helper1" - self.command_responses = contextvars.ContextVar('command_responses', default=[]) - self.helperthread = None - + self.client_id = "helper1@bumper/helper1" + self.command_responses = contextvars.ContextVar("command_responses", default=[]) + self.helperthread = None def run(self, run_async=False): - if run_async: - hloop = asyncio.new_event_loop() - helperbotlog.debug("Starting MQTT HelperBot Thread: 1") - self.helperthread = Thread(name="MQTTHelperBot_Thread",target=self.run_helperbot, args=(hloop,)) - self.helperthread.setDaemon(True) - self.helperthread.start() - + if run_async: + hloop = asyncio.new_event_loop() + helperbotlog.debug("Starting MQTT HelperBot Thread: 1") + self.helperthread = Thread( + name="MQTTHelperBot_Thread", target=self.run_helperbot, args=(hloop,) + ) + self.helperthread.setDaemon(True) + self.helperthread.start() + else: self.run_helperbot() - - def run_helperbot(self, loop): + def run_helperbot(self, loop): logging.info("Starting MQTT HelperBot") - print("Starting MQTT HelperBot") + print("Starting MQTT HelperBot") try: - asyncio.set_event_loop(loop) - self.Client = MQTTClient(client_id=self.client_id, config={'check_hostname':False}) - loop.run_until_complete(self.start_helper_bot()) - loop.run_until_complete(self.get_msg()) - loop.run_forever() + asyncio.set_event_loop(loop) + self.Client = MQTTClient( + client_id=self.client_id, config={"check_hostname": False} + ) + loop.run_until_complete(self.start_helper_bot()) + loop.run_until_complete(self.get_msg()) + loop.run_forever() except Exception as e: - helperbotlog.exception('{}'.format(e)) - - async def start_helper_bot(self): + helperbotlog.exception("{}".format(e)) + + async def start_helper_bot(self): try: - await self.Client.connect('mqtts://{}:{}/'.format(self.address[0], self.address[1]), cafile=bumper.ca_cert) - await self.Client.subscribe([ - ('iot/p2p/+/+/+/+/helper1/bumper/helper1/+/+/+',QOS_0), - ('iot/p2p/+',QOS_0) - ]) + await self.Client.connect( + "mqtts://{}:{}/".format(self.address[0], self.address[1]), + cafile=bumper.ca_cert, + ) + await self.Client.subscribe( + [ + ("iot/p2p/+/+/+/+/helper1/bumper/helper1/+/+/+", QOS_0), + ("iot/p2p/+", QOS_0), + ] + ) except Exception as e: - helperbotlog.exception('{}'.format(e)) + helperbotlog.exception("{}".format(e)) async def get_msg(self): try: while True: - message = await self.Client.deliver_message() + message = await self.Client.deliver_message() - #helperbotlog.debug("HelperBot MQTT Received Message on Topic: {} - Message: {}".format(message.topic, str(message.payload.decode("utf-8")))) - cresp = self.command_responses.get() - - if (str(message.topic).split("/")[6] == "helper1"): - cresp.append({"time": time.time() ,"topic": message.topic,"payload":str(message.data.decode("utf-8"))}) - - #Cleanup "expired messages" > 60 seconds from time - for msg in cresp: - expire_time = (datetime.fromtimestamp(msg['time']) + timedelta(seconds=10)).timestamp() + # helperbotlog.debug("HelperBot MQTT Received Message on Topic: {} - Message: {}".format(message.topic, str(message.payload.decode("utf-8")))) + cresp = self.command_responses.get() + + if str(message.topic).split("/")[6] == "helper1": + cresp.append( + { + "time": time.time(), + "topic": message.topic, + "payload": str(message.data.decode("utf-8")), + } + ) + + # Cleanup "expired messages" > 60 seconds from time + for msg in cresp: + expire_time = ( + datetime.fromtimestamp(msg["time"]) + timedelta(seconds=10) + ).timestamp() if time.time() > expire_time: - #helperbotlog.debug("Pruning Message Time: {}, MsgTime: {}, MsgTime+60: {}".format(time.time(), msg['time'], expire_time)) - cresp.remove(msg) - - self.command_responses.set(cresp) - #helperbotlog.debug("MQTT Command Response List Count: %s" %len(cresp)) - - except Exception as e: - helperbotlog.exception('{}'.format(e)) + # helperbotlog.debug("Pruning Message Time: {}, MsgTime: {}, MsgTime+60: {}".format(time.time(), msg['time'], expire_time)) + cresp.remove(msg) - async def wait_for_resp(self, requestid): + self.command_responses.set(cresp) + # helperbotlog.debug("MQTT Command Response List Count: %s" %len(cresp)) + + except Exception as e: + helperbotlog.exception("{}".format(e)) + + async def wait_for_resp(self, requestid): try: t_end = (datetime.now() + timedelta(seconds=10)).timestamp() while time.time() < t_end: await asyncio.sleep(0.1) - responses = self.command_responses.get() - if len(responses) > 0: + responses = self.command_responses.get() + if len(responses) > 0: for msg in responses: - topic = str(msg['topic']).split("/") - if (topic[6] == "helper1" and topic[10] == requestid): - #helperbotlog.debug('VacBot MQTT Response: Topic: %s Payload: %s' % (msg['topic'], msg['payload'])) + topic = str(msg["topic"]).split("/") + if topic[6] == "helper1" and topic[10] == requestid: + # helperbotlog.debug('VacBot MQTT Response: Topic: %s Payload: %s' % (msg['topic'], msg['payload'])) if topic[11] == "j": - resppayload = json.loads(msg['payload']) - else: - resppayload = str(msg['payload']) - resp = { - "id": requestid, - "ret": "ok", - "resp": resppayload - } + resppayload = json.loads(msg["payload"]) + else: + resppayload = str(msg["payload"]) + resp = {"id": requestid, "ret": "ok", "resp": resppayload} cresp = self.command_responses.get() - cresp.remove(msg) - self.command_responses.set(cresp) + cresp.remove(msg) + self.command_responses.set(cresp) return resp - - return { "id": requestid, "errno": "timeout", "ret": "fail" } + return {"id": requestid, "errno": "timeout", "ret": "fail"} except asyncio.CancelledError as e: - helperbotlog.debug('wait_for_resp cancelled by asyncio') + helperbotlog.debug("wait_for_resp cancelled by asyncio") except Exception as e: - helperbotlog.exception('{}'.format(e)) + helperbotlog.exception("{}".format(e)) async def send_command(self, cmdjson, requestid): try: - ttopic = "iot/p2p/{}/helper1/bumper/helper1/{}/{}/{}/q/{}/{}".format(cmdjson["cmdName"], - cmdjson["toId"], cmdjson["toType"], cmdjson["toRes"], requestid, cmdjson["payloadType"]) - try: - await self.Client.publish(ttopic, str(cmdjson["payload"]).encode(),QOS_0) + ttopic = "iot/p2p/{}/helper1/bumper/helper1/{}/{}/{}/q/{}/{}".format( + cmdjson["cmdName"], + cmdjson["toId"], + cmdjson["toType"], + cmdjson["toRes"], + requestid, + cmdjson["payloadType"], + ) + try: + await self.Client.publish( + ttopic, str(cmdjson["payload"]).encode(), QOS_0 + ) except Exception as e: helperbotlog.exception("{}".format(e)) - resp = await self.wait_for_resp(requestid) - - return resp + resp = await self.wait_for_resp(requestid) + + return resp except Exception as e: - helperbotlog.exception('{}'.format(e)) - + helperbotlog.exception("{}".format(e)) -class MQTTServer(): - default_config = {} + +class MQTTServer: + default_config = {} bumper_users = [] bumper_clients = [] bumper_bots = [] - - async def broker_coro(self): - try: + async def broker_coro(self): + try: broker = hbmqtt.broker.Broker(config=self.default_config) - await broker.start() - + await broker.start() + except PermissionError as e: if "bind" in e.strerror: - mqttserverlog.exception("Error binding mqttserver, exiting. Try using a different hostname or IP - {}".format(e)) - exit(1) - - except Exception as e: - mqttserverlog.exception('{}'.format(e)) + mqttserverlog.exception( + "Error binding mqttserver, exiting. Try using a different hostname or IP - {}".format( + e + ) + ) exit(1) - async def active_bot_listing(self): - try: + except Exception as e: + mqttserverlog.exception("{}".format(e)) + exit(1) + + async def active_bot_listing(self): + try: while True: await asyncio.sleep(5) - mqttserverlog.debug('connected bots - %s' % self.bumper_bots.get()) + mqttserverlog.debug("connected bots - %s" % self.bumper_bots.get()) except Exception as e: - mqttserverlog.exception('{}'.format(e)) + mqttserverlog.exception("{}".format(e)) - def __init__(self, address,bumper_users=contextvars.ContextVar, bumper_bots=contextvars.ContextVar, bumper_clients=contextvars.ContextVar): + def __init__( + self, + address, + bumper_users=contextvars.ContextVar, + bumper_bots=contextvars.ContextVar, + bumper_clients=contextvars.ContextVar, + ): try: self.bumper_users = bumper_users self.bumper_bots = bumper_bots - self.bumper_clients = bumper_clients - self.mqttserverthread = None + self.bumper_clients = bumper_clients + self.mqttserverthread = None self.address = address - #The below adds a plugin to the hbmqtt.broker.plugins without having to futz with setup.py + # The below adds a plugin to the hbmqtt.broker.plugins without having to futz with setup.py distribution = pkg_resources.Distribution("hbmqtt.broker.plugins") - bumper_plugin = pkg_resources.EntryPoint.parse('bumper = bumper.mqttserver:BumperMQTTServer_Plugin', dist=distribution) + bumper_plugin = pkg_resources.EntryPoint.parse( + "bumper = bumper.mqttserver:BumperMQTTServer_Plugin", dist=distribution + ) distribution._ep_map = {"hbmqtt.broker.plugins": {"bumper": bumper_plugin}} pkg_resources.working_set.add(distribution) # Initialize bot server self.default_config = { - 'listeners': { - 'default': { - 'type': 'tcp', - }, - 'tls1': { - 'bind': "{}:{}".format(address[0], address[1]), - 'ssl': 'on', - 'certfile': bumper.server_cert, - 'keyfile': bumper.server_key, + "listeners": { + "default": {"type": "tcp"}, + "tls1": { + "bind": "{}:{}".format(address[0], address[1]), + "ssl": "on", + "certfile": bumper.server_cert, + "keyfile": bumper.server_key, }, }, - 'sys_interval': 10, - 'auth': { - 'allow-anonymous': False, - 'password-file': os.path.join(os.path.dirname(os.path.realpath(__file__)), "passwd"), - 'plugins': [ - 'bumper' #No plugins == no auth - ] + "sys_interval": 10, + "auth": { + "allow-anonymous": False, + "password-file": os.path.join( + os.path.dirname(os.path.realpath(__file__)), "passwd" + ), + "plugins": ["bumper"], # No plugins == no auth }, - 'topic-check': { - 'enabled': False + "topic-check": {"enabled": False}, + "bumper": { + "bumper_users": self.bumper_users, + "bumper_bots": self.bumper_bots, + "bumper_clients": self.bumper_clients, }, - 'bumper':{ - 'bumper_users' : self.bumper_users, - 'bumper_bots': self.bumper_bots, - 'bumper_clients': self.bumper_clients, - }, } - - except Exception as e: - mqttserverlog.exception('{}'.format(e)) - def run(self, run_async=False,): + except Exception as e: + mqttserverlog.exception("{}".format(e)) + + def run(self, run_async=False): if run_async: - sloop = asyncio.new_event_loop() - mqttserverlog.debug("Starting MQTTServer Thread: 1") - self.mqttserverthread = Thread(name="MQTTServer_Thread",target=self.run_server, args=(sloop,)) - self.mqttserverthread.setDaemon(True) - self.mqttserverthread.start() - + sloop = asyncio.new_event_loop() + mqttserverlog.debug("Starting MQTTServer Thread: 1") + self.mqttserverthread = Thread( + name="MQTTServer_Thread", target=self.run_server, args=(sloop,) + ) + self.mqttserverthread.setDaemon(True) + self.mqttserverthread.start() + else: self.run_server() - def run_server(self, loop): - + def run_server(self, loop): + logging.info("Starting MQTT Server at {}".format(self.address)) print("Starting MQTT Server at {}".format(self.address)) - try: + try: asyncio.set_event_loop(loop) loop.run_until_complete(self.broker_coro()) - #loop.run_until_complete(self.active_bot_listing()) - loop.run_forever() - - except Exception as e: - mqttserverlog.exception('{}'.format(e)) + # loop.run_until_complete(self.active_bot_listing()) + loop.run_forever() -class BumperMQTTServer_Plugin: + except Exception as e: + mqttserverlog.exception("{}".format(e)) + + +class BumperMQTTServer_Plugin: def __init__(self, context): - self.context = context + self.context = context try: - self.bumper_config = self.context.config['bumper'] - self.auth_config = self.context.config['auth'] + self.bumper_config = self.context.config["bumper"] + self.auth_config = self.context.config["auth"] except KeyError: - self.context.logger.warning("'bumper' section not found in context configuration") + self.context.logger.warning( + "'bumper' section not found in context configuration" + ) except Exception as e: - mqttserverlog.exception('{}'.format(e)) - + mqttserverlog.exception("{}".format(e)) async def authenticate(self, *args, **kwargs): if not self.auth_config: # auth config section not found - self.context.logger.warning("'auth' section not found in context configuration") + self.context.logger.warning( + "'auth' section not found in context configuration" + ) return False - - allow_anonymous = self.auth_config.get('allow-anonymous', True) # allow anonymous by default + + allow_anonymous = self.auth_config.get( + "allow-anonymous", True + ) # allow anonymous by default if allow_anonymous: authenticated = True self.context.logger.debug("Authentication success: config allows anonymous") else: try: - bumper_users = self.bumper_config['bumper_users'].get() - bumper_bots = self.bumper_config['bumper_bots'].get() - bumper_clients = self.bumper_config['bumper_clients'].get() - session = kwargs.get('session', None) + bumper_users = self.bumper_config["bumper_users"].get() + bumper_bots = self.bumper_config["bumper_bots"].get() + bumper_clients = self.bumper_config["bumper_clients"].get() + session = kwargs.get("session", None) username = session.username password = session.password client_id = session.client_id - + didsplit = str(client_id).split("@") - #If this isn't a fake user (fuid) then add as a bot - if not (str(didsplit[0]).startswith("fuid") or str(didsplit[0]).startswith("helper")): - tmpbotdetail = str(didsplit[1]).split("/") - bumper.add_bot(username, didsplit[0], tmpbotdetail[0], tmpbotdetail[1]) - mqttserverlog.debug("new bot authenticated SN: {} DID: {}".format(username, didsplit[0])) + # If this isn't a fake user (fuid) then add as a bot + if not ( + str(didsplit[0]).startswith("fuid") + or str(didsplit[0]).startswith("helper") + ): + tmpbotdetail = str(didsplit[1]).split("/") + bumper.add_bot( + username, didsplit[0], tmpbotdetail[0], tmpbotdetail[1] + ) + mqttserverlog.debug( + "new bot authenticated SN: {} DID: {}".format( + username, didsplit[0] + ) + ) authenticated = True - + else: tmpclientdetail = str(didsplit[1]).split("/") - userid = didsplit[0] - realm = tmpclientdetail[0] + userid = didsplit[0] + realm = tmpclientdetail[0] resource = tmpclientdetail[1] - + if userid == "helper1": authenticated = True else: @@ -304,61 +357,63 @@ class BumperMQTTServer_Plugin: elif bumper.use_auth == False: auth = True - if auth: - bumper.add_client(userid, realm, resource) - mqttserverlog.debug("client authenticated {}".format(userid)) + if auth: + bumper.add_client(userid, realm, resource) + mqttserverlog.debug( + "client authenticated {}".format(userid) + ) authenticated = True else: authenticated = False except Exception as e: - mqttserverlog.exception('{}'.format(e)) + mqttserverlog.exception("{}".format(e)) authenticated = False - + return authenticated async def on_broker_client_connected(self, client_id): try: - bumper_users = self.bumper_config['bumper_users'].get() - bumper_bots = self.bumper_config['bumper_bots'].get() - bumper_clients = self.bumper_config['bumper_clients'].get() + bumper_users = self.bumper_config["bumper_users"].get() + bumper_bots = self.bumper_config["bumper_bots"].get() + bumper_clients = self.bumper_config["bumper_clients"].get() didsplit = str(client_id).split("@") for bot in bumper_bots: if didsplit[0] == bot.did: bot.mqtt_connection = True mqttserverlog.debug("bot connected {}".format(bot.did)) - self.bumper_config['bumper_bots'].set(bumper_bots) + self.bumper_config["bumper_bots"].set(bumper_bots) for client in bumper_clients: - if didsplit[0] == client.userid and client.userid != 'helper1': + if didsplit[0] == client.userid and client.userid != "helper1": client.mqtt_connection = True - #mqttserverlog.info("client connected {}".format(client.userid)) - self.bumper_config['bumper_clients'].set(bumper_clients) + # mqttserverlog.info("client connected {}".format(client.userid)) + self.bumper_config["bumper_clients"].set(bumper_clients) except Exception as e: - mqttserverlog.exception('{}'.format(e)) - + mqttserverlog.exception("{}".format(e)) async def on_broker_client_disconnected(self, client_id): try: - bumper_users = self.bumper_config['bumper_users'].get() - bumper_bots = self.bumper_config['bumper_bots'].get() - bumper_clients = self.bumper_config['bumper_clients'].get() + bumper_users = self.bumper_config["bumper_users"].get() + bumper_bots = self.bumper_config["bumper_bots"].get() + bumper_clients = self.bumper_config["bumper_clients"].get() didsplit = str(client_id).split("@") for bot in bumper_bots: if didsplit[0] == bot.did: bot.mqtt_connection = False mqttserverlog.debug("bot disconnected {}".format(bot.did)) - self.bumper_config['bumper_bots'].set(bumper_bots) + self.bumper_config["bumper_bots"].set(bumper_bots) for client in bumper_clients: - if didsplit[0] == client.userid and client.userid != 'helper1': + if didsplit[0] == client.userid and client.userid != "helper1": client.mqtt_connection = False - #mqttserverlog.info("client disconnected {}".format(client.userid)) - self.bumper_config['bumper_clients'].set(bumper_clients) + # mqttserverlog.info("client disconnected {}".format(client.userid)) + self.bumper_config["bumper_clients"].set(bumper_clients) except Exception as e: - mqttserverlog.exception('{}'.format(e)) \ No newline at end of file + mqttserverlog.exception("{}".format(e)) + diff --git a/bumper/xmppserver.py b/bumper/xmppserver.py index 46a9ae1..f20081f 100644 --- a/bumper/xmppserver.py +++ b/bumper/xmppserver.py @@ -9,122 +9,160 @@ import bumper xmppserverlog = logging.getLogger("xmppserver") -class XMPPServer(): - server_id = 'bumper' - bot_id = 'bumpy' + +class XMPPServer: + server_id = "bumper" + bot_id = "bumpy" client_id = None clients = [] exit_flag = False - def __init__(self, address, bumper_users=contextvars.ContextVar, bumper_bots=contextvars.ContextVar, bumper_clients=contextvars.ContextVar): + def __init__( + self, + address, + bumper_users=contextvars.ContextVar, + bumper_bots=contextvars.ContextVar, + bumper_clients=contextvars.ContextVar, + ): # Initialize bot server self.address = address self.bumper_users = bumper_users self.bumper_bots = bumper_bots - self.bumper_clients = bumper_clients - + self.bumper_clients = bumper_clients - def run(self, run_async=False): + def run(self, run_async=False): if run_async: - xmppserverlog.debug("Starting XMPPServer Thread: 1") - self.xmppthread = Thread(name="XMPPServer_Thread",target=self.run_server) - self.xmppthread.setDaemon(True) - self.xmppthread.start() - + xmppserverlog.debug("Starting XMPPServer Thread: 1") + self.xmppthread = Thread(name="XMPPServer_Thread", target=self.run_server) + self.xmppthread.setDaemon(True) + self.xmppthread.start() + else: try: self.run_server() except KeyboardInterrupt: self.disconnect() - def run_server(self): + def run_server(self): logging.info("Starting XMPP Server at {}".format(self.address)) print("Starting XMPP Server at {}".format(self.address)) - - #xmppserverlog.setLevel(logging.DEBUG) - #Set SSL Context + # xmppserverlog.setLevel(logging.DEBUG) + + # Set SSL Context self.ssl_ctx = ssl.create_default_context(ssl.Purpose.CLIENT_AUTH) - self.ssl_ctx.load_cert_chain(certfile=bumper.server_cert,keyfile=bumper.server_key) - - self.socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM) - self.socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) + self.ssl_ctx.load_cert_chain( + certfile=bumper.server_cert, keyfile=bumper.server_key + ) + + self.socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM) + self.socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) try: self.socket.bind(self.address) - self.socket.listen(5) + self.socket.listen(5) - xmppserverlog.debug('listening on {}:{}'.format(self.address[0], self.address[1])) - while not self.exit_flag: + xmppserverlog.debug( + "listening on {}:{}".format(self.address[0], self.address[1]) + ) + while not self.exit_flag: connection, client_address = self.socket.accept() - + # disconnect any clients with this ip for client in self.clients: if client.address == client_address[0]: - xmppserverlog.debug('disconnecting existing client {} with resource {}'.format(client.address, client.clientresource)) + xmppserverlog.debug( + "disconnecting existing client {} with resource {}".format( + client.address, client.clientresource + ) + ) client._disconnect() - self.remove_client_byip(client.address) - - xmppserverlog.debug('starting new client with ip {}'.format(client_address[0])) - thread_id = uuid.uuid4() - client = Client(thread_id, connection, client_address, self.bumper_users, self.bumper_bots, self.bumper_clients) - client.setDaemon(True) + self.remove_client_byip(client.address) + + xmppserverlog.debug( + "starting new client with ip {}".format(client_address[0]) + ) + thread_id = uuid.uuid4() + client = Client( + thread_id, + connection, + client_address, + self.bumper_users, + self.bumper_bots, + self.bumper_clients, + ) + client.setDaemon(True) client.start() - self.clients.append(client) + self.clients.append(client) except PermissionError as e: if "bind" in e.strerror: - xmppserverlog.exception("Error binding XMPPServer, exiting. Try using a different hostname or IP - {}".format(e)) + xmppserverlog.exception( + "Error binding XMPPServer, exiting. Try using a different hostname or IP - {}".format( + e + ) + ) exit(1) - + except Exception as e: - xmppserverlog.exception('{}'.format(e)) + xmppserverlog.exception("{}".format(e)) exit(1) - + except KeyboardInterrupt as e: - xmppserverlog.exception('{}'.format(e)) - + xmppserverlog.exception("{}".format(e)) + finally: connection.shutdown(socket.SHUT_RDWR) connection.close() self.disconnect() - xmppserverlog.info('disconnecting') + xmppserverlog.info("disconnecting") - self.socket.close() + self.socket.close() def disconnect(self): try: - xmppserverlog.debug('waiting for all client threads to exit') + xmppserverlog.debug("waiting for all client threads to exit") for client in self.clients: client._disconnect() self.exit_flag = True - xmppserverlog.debug('shutting down') - - except Exception as e: - xmppserverlog.exception("{}".format(e)) + xmppserverlog.debug("shutting down") + except Exception as e: + xmppserverlog.exception("{}".format(e)) def remove_client_byip(self, ip): for client in self.clients: if client.address == ip: - xmppserverlog.debug('removing client from client list with ip {} and resource {}'.format(client.address, client.clientresource)) + xmppserverlog.debug( + "removing client from client list with ip {} and resource {}".format( + client.address, client.clientresource + ) + ) client._disconnect() self.clients.remove(client) def remove_client_byresource(self, resource): for client in self.clients: if str(client.clientresource).lower() == str(resource).lower(): - xmppserverlog.debug('removing client from client list with ip {} and resource {}'.format(client.address, client.clientresource)) + xmppserverlog.debug( + "removing client from client list with ip {} and resource {}".format( + client.address, client.clientresource + ) + ) client._disconnect() - self.clients.remove(client) + self.clients.remove(client) def remove_client_byuid(self, uid): for client in self.clients: if str(client.uid).lower() == str(uid).lower(): - xmppserverlog.debug('removing client from client list with ip {} and resource {}'.format(client.address, client.clientresource)) + xmppserverlog.debug( + "removing client from client list with ip {} and resource {}".format( + client.address, client.clientresource + ) + ) client._disconnect() - self.clients.remove(client) + self.clients.remove(client) class Client(threading.Thread): @@ -138,7 +176,15 @@ class Client(threading.Thread): BOT = 1 CONTROLLER = 2 - def __init__(self, thread_id, connection, client_address,bumper_users=contextvars.ContextVar, bumper_bots=contextvars.ContextVar, bumper_clients=contextvars.ContextVar): + def __init__( + self, + thread_id, + connection, + client_address, + bumper_users=contextvars.ContextVar, + bumper_bots=contextvars.ContextVar, + bumper_clients=contextvars.ContextVar, + ): threading.Thread.__init__(self) self.id = thread_id self.name = "XMPP_Client_{}".format(client_address[0]) @@ -147,37 +193,38 @@ class Client(threading.Thread): self.connection = connection self.address = client_address[0] self.clientresource = "" - self.uid = "" - self.log_sent_message = False #Set to true to log sends - self.log_incoming_data = True #Set to true to log sends + self.uid = "" + self.log_sent_message = False # Set to true to log sends + self.log_incoming_data = True # Set to true to log sends self.bumper_users = bumper_users self.bumper_bots = bumper_bots - self.bumper_clients = bumper_clients + self.bumper_clients = bumper_clients - xmppserverlog.debug('new client thread init for client with ip {}'.format(self.address)) + xmppserverlog.debug( + "new client thread init for client with ip {}".format(self.address) + ) def send(self, command): try: if not self.connection._closed: if self.log_sent_message: - xmppserverlog.debug('send {} - {}'.format(self.address, command)) - self.connection.send(command.encode()) - + xmppserverlog.debug("send {} - {}".format(self.address, command)) + self.connection.send(command.encode()) + except OSError as e: - xmppserverlog.error('{}'.format(e)) + xmppserverlog.error("{}".format(e)) except BrokenPipeError as e: - xmppserverlog.error('{}'.format(e)) - #self._set_state('DISCONNECT') + xmppserverlog.error("{}".format(e)) + # self._set_state('DISCONNECT') except ConnectionResetError as e: - xmppserverlog.error('{}'.format(e)) - #self._set_state('DISCONNECT') - + xmppserverlog.error("{}".format(e)) + # self._set_state('DISCONNECT') + except Exception as e: xmppserverlog.exception("{}".format(e)) - def _disconnect(self): try: bumper_bots = self.bumper_bots.get() @@ -185,194 +232,249 @@ class Client(threading.Thread): for bot in bumper_bots: if self.uid == bot.did: bot.xmpp_connection = False - #xmppserverlog.info("bot disconnected {}".format(bot.did)) + # xmppserverlog.info("bot disconnected {}".format(bot.did)) self.bumper_bots.set(bumper_bots) for client in bumper_clients: - if self.uid == client.userid and client.userid != 'helper1': + if self.uid == client.userid and client.userid != "helper1": client.xmpp_connection = False - #xmppserverlog.info("client disconnected {}".format(client.userid)) + # xmppserverlog.info("client disconnected {}".format(client.userid)) self.bumper_clients.set(bumper_clients) - #xmppserverlog.debug('client {} with resource {} disconnecting'.format(self.address, self.clientresource)) + # xmppserverlog.debug('client {} with resource {} disconnecting'.format(self.address, self.clientresource)) self.connection.close() - + except Exception as e: - xmppserverlog.exception("{}".format(e)) + xmppserverlog.exception("{}".format(e)) def _tag_strip_uri(self, tag): try: - if tag[0] == '{': - uri, ignore, tag = tag[1:].partition('}') + if tag[0] == "{": + uri, ignore, tag = tag[1:].partition("}") return tag - + except Exception as e: - xmppserverlog.exception("{}".format(e)) + xmppserverlog.exception("{}".format(e)) def _set_state(self, state): try: new_state = getattr(Client, state) if self.state > new_state: - raise Exception('{} illegal state change {}->{}'.format(self.address, self.state, new_state)) + raise Exception( + "{} illegal state change {}->{}".format( + self.address, self.state, new_state + ) + ) - xmppserverlog.debug('{} state: {}'.format(self.address, state)) + xmppserverlog.debug("{} state: {}".format(self.address, state)) self.state = new_state if new_state == 5: self._disconnect() - + except Exception as e: xmppserverlog.exception("{}".format(e)) - def _handle_ctl(self, xml, data): + def _handle_ctl(self, xml, data): try: - if data.decode('utf-8').find('roster') > -1: - #Return not-implemented for roster - self.send(''.format(xml.get('id'))) + if data.decode("utf-8").find("roster") > -1: + # Return not-implemented for roster + self.send( + ''.format( + xml.get("id") + ) + ) return - - if xml.get('type') == 'set': - if data.decode('utf-8').find('com:sf') > -1 and xml.get('to') == 'rl.ecorobot.net': #Android bind? Not sure what this does yet. - self.send(''.format(xml.get('id'), self.uid, XMPPServer.bot_id, self.clientresource)) - + + if xml.get("type") == "set": + if ( + data.decode("utf-8").find("com:sf") > -1 + and xml.get("to") == "rl.ecorobot.net" + ): # Android bind? Not sure what this does yet. + self.send( + ''.format( + xml.get("id"), + self.uid, + XMPPServer.bot_id, + self.clientresource, + ) + ) + else: - xmppserverlog.debug('Unknown set type: {}'.format(data.decode('utf-8'))) + xmppserverlog.debug( + "Unknown set type: {}".format(data.decode("utf-8")) + ) if xml[0][0]: ctl = xml[0][0] - if ctl.get('admin') and self.type == self.BOT: - xmppserverlog.debug('admin username received from bot: {}'.format(ctl.get('admin'))) - XMPPServer.client_id = ctl.get('admin') - return + if ctl.get("admin") and self.type == self.BOT: + xmppserverlog.debug( + "admin username received from bot: {}".format(ctl.get("admin")) + ) + XMPPServer.client_id = ctl.get("admin") + return - #forward + # forward for client in XMPPServer.clients: if client.address != self.address and client.state == client.READY: if client.type == self.BOT: - data = data.decode('utf-8') - id_index = data.find('id') + data = data.decode("utf-8") + id_index = data.find("id") if id_index > -1: - data = data[:id_index] + 'from="' + XMPPServer.client_id + '" ' + data[id_index:] + data = ( + data[:id_index] + + 'from="' + + XMPPServer.client_id + + '" ' + + data[id_index:] + ) data = data.encode() - client.send(data.decode('utf-8')) - + client.send(data.decode("utf-8")) + except Exception as e: xmppserverlog.exception("{}".format(e)) - def _handle_ping(self, xml, data): try: - if(xml.get('to').find('@') == -1): + if xml.get("to").find("@") == -1: # Ping to server - respond - self.send(''.format(xml.get('id'), xml.get('to'))) + self.send( + ''.format( + xml.get("id"), xml.get("to") + ) + ) else: for client in XMPPServer.clients: if client.address != self.address and client.state == client.READY: - client.send(data.decode('utf-8')) - - except Exception as e: - xmppserverlog.exception('{}'.format(e)) + client.send(data.decode("utf-8")) - def _handle_result(self, data): + except Exception as e: + xmppserverlog.exception("{}".format(e)) + + def _handle_result(self, data): # forward try: for client in XMPPServer.clients: if client.address != self.address and client.state == client.READY: - client.send(data.decode('utf-8')) - - except Exception as e: - xmppserverlog.exception("{}".format(e)) - + client.send(data.decode("utf-8")) - def _handle_connect(self, data): + except Exception as e: + xmppserverlog.exception("{}".format(e)) + + def _handle_connect(self, data): try: if self.state == self.CONNECT: - #Client first connecting, send our features + # Client first connecting, send our features - if data.decode('utf-8').find('jabber:client') > -1: + if data.decode("utf-8").find("jabber:client") > -1: # ack jabbr:client - #no STARTTLS - self.send(''.format(XMPPServer.server_id)) - #with STARTTLS - #self.send(''.format(XMPPServer.server_id)) + # no STARTTLS + self.send( + ''.format( + XMPPServer.server_id + ) + ) + # with STARTTLS + # self.send(''.format(XMPPServer.server_id)) time.sleep(0.25) # send authentication support for iq-auth (fallback) and SASL - self.send('PLAIN') - #self.send('') + self.send( + 'PLAIN' + ) + # self.send('') - elif data.decode('utf-8').find('jabber:iq:auth') > -1: #Handle iq-auth + elif data.decode("utf-8").find("jabber:iq:auth") > -1: # Handle iq-auth self._handle_iq_auth(data) - elif data.decode('utf-8').find('urn:ietf:params:xml:ns:xmpp-sasl') > -1: #Handle SASL auth + elif ( + data.decode("utf-8").find("urn:ietf:params:xml:ns:xmpp-sasl") > -1 + ): # Handle SASL auth self._handle_sasl_auth(data) elif self.state == self.INIT: - #Client getting session after authentication - if data.decode('utf-8').find('jabber:client') > -1: + # Client getting session after authentication + if data.decode("utf-8").find("jabber:client") > -1: # ack jabbr:client - self.send(''.format(XMPPServer.server_id)) + self.send( + ''.format( + XMPPServer.server_id + ) + ) time.sleep(0.25) # session - self.send('') + self.send( + '' + ) - else: #Handle init bind - xml = ET.fromstring(data.decode('utf-8')) + else: # Handle init bind + xml = ET.fromstring(data.decode("utf-8")) if len(xml): child = self._tag_strip_uri(xml[0].tag) else: child = None - - if xml.tag == 'iq': - if child == 'bind': + + if xml.tag == "iq": + if child == "bind": self._handle_bind(xml) - + except Exception as e: - xmppserverlog.exception('{}'.format(e)) + xmppserverlog.exception("{}".format(e)) - - def _handle_iq_auth(self, data): + def _handle_iq_auth(self, data): try: - xml = ET.fromstring(data.decode('utf-8')) - ctl = xml[0][0] + xml = ET.fromstring(data.decode("utf-8")) + ctl = xml[0][0] xmppserverlog.info("IQ AUTH XML: {}".format(xml)) - #Received username and auth tag, send username/password requirement - if xml.get('type') == 'get' and "auth}username" in ctl.tag and self.type == self.UNKNOWN: - self.send(''.format(xml.get('id'))) + # Received username and auth tag, send username/password requirement + if ( + xml.get("type") == "get" + and "auth}username" in ctl.tag + and self.type == self.UNKNOWN + ): + self.send( + ''.format( + xml.get("id") + ) + ) - #Received username, password, resource - Handle auth here and return pass or fail - if xml.get('type') == 'set' and "auth}username" in ctl.tag and self.type == self.UNKNOWN: + # Received username, password, resource - Handle auth here and return pass or fail + if ( + xml.get("type") == "set" + and "auth}username" in ctl.tag + and self.type == self.UNKNOWN + ): xmlauth = xml[0].getchildren() - uid = '' - password = '' - resource = '' + uid = "" + password = "" + resource = "" for aitem in xmlauth: - if 'username' in aitem.tag: + if "username" in aitem.tag: self.uid = aitem.text - elif 'password' in aitem.tag: + elif "password" in aitem.tag: password = aitem.text.split("/")[2] authcode = password - elif 'resource' in aitem.tag: + elif "resource" in aitem.tag: self.clientresource = aitem.text resource = self.clientresource if not self.uid.startswith("fuid"): - - #Need sample data to see details here - bumper.add_bot('',self.uid, '', resource) + + # Need sample data to see details here + bumper.add_bot("", self.uid, "", resource) xmppserverlog.info("bot authenticated {}".format(self.uid)) - - #Client authenticated, move to next state - self._set_state('INIT') - - #Successful auth - self.send(''.format(xml.get('id'))) + + # Client authenticated, move to next state + self._set_state("INIT") + + # Successful auth + self.send(''.format(xml.get("id"))) else: auth = False @@ -381,52 +483,65 @@ class Client(threading.Thread): elif bumper.use_auth == False: auth = True - if auth: - bumper.add_client(self.uid, 'bumper', self.clientresource) + if auth: + bumper.add_client(self.uid, "bumper", self.clientresource) xmppserverlog.debug("client authenticated {}".format(self.uid)) - #Client authenticated, move to next state - self._set_state('INIT') + # Client authenticated, move to next state + self._set_state("INIT") + + # Successful auth + self.send(''.format(xml.get("id"))) - #Successful auth - self.send(''.format(xml.get('id'))) - else: - #Failed auth - self.send(''.format(xml.get('id'))) - + # Failed auth + self.send( + ''.format( + xml.get("id") + ) + ) except ET.ParseError as e: if "no element found" in e.msg: - xmppserverlog.debug('xml parse error - {} - {} - this is common with ecovac protocol'.format(data.decode('utf-8'), e)) + xmppserverlog.debug( + "xml parse error - {} - {} - this is common with ecovac protocol".format( + data.decode("utf-8"), e + ) + ) elif "not well-formed (invalid token)" in e.msg: - xmppserverlog.debug('xml parse error - {} - {}'.format(data.decode('utf-8'), e)) + xmppserverlog.debug( + "xml parse error - {} - {}".format(data.decode("utf-8"), e) + ) else: - xmppserverlog.debug('xml parse error - {} - {}'.format(data.decode('utf-8'), e)) + xmppserverlog.debug( + "xml parse error - {} - {}".format(data.decode("utf-8"), e) + ) except Exception as e: - xmppserverlog.exception('{}'.format(e)) + xmppserverlog.exception("{}".format(e)) - def _handle_sasl_auth(self, data): + def _handle_sasl_auth(self, data): try: - xml = ET.fromstring(data.decode('utf-8')) - saslauth = base64.b64decode(xml.text).decode('utf-8').split("/") + xml = ET.fromstring(data.decode("utf-8")) + saslauth = base64.b64decode(xml.text).decode("utf-8").split("/") username = saslauth[0] - username = saslauth[0].split('\x00')[1] - self.uid = username + username = saslauth[0].split("\x00")[1] + self.uid = username resource = saslauth[1] self.clientresource = resource authcode = saslauth[2] if not self.uid.startswith("fuid"): - #Need sample data to see details here - bumper.add_bot('',self.uid, '', resource) + # Need sample data to see details here + bumper.add_bot("", self.uid, "", resource) xmppserverlog.info("bot authenticated {}".format(self.uid)) - #Send response - self.send('') #Success + # Send response + self.send( + '' + ) # Success - #Client authenticated, move to next state - self._set_state('INIT') + # Client authenticated, move to next state + self._set_state("INIT") else: auth = False @@ -435,179 +550,224 @@ class Client(threading.Thread): elif bumper.use_auth == False: auth = True - if auth: - bumper.add_client(self.uid, 'bumper', self.clientresource) + if auth: + bumper.add_client(self.uid, "bumper", self.clientresource) xmppserverlog.debug("client authenticated {}".format(self.uid)) - - #Client authenticated, move to next state - self._set_state('INIT') - #Send response - self.send('') #Success - + # Client authenticated, move to next state + self._set_state("INIT") + + # Send response + self.send( + '' + ) # Success + else: - #Failed to authenticate - self.send('') #Fail - + # Failed to authenticate + self.send( + '' + ) # Fail + except ET.ParseError as e: if "no element found" in e.msg: - xmppserverlog.debug('xml parse error - {} - {} - this is common with ecovac protocol'.format(data.decode('utf-8'), e)) + xmppserverlog.debug( + "xml parse error - {} - {} - this is common with ecovac protocol".format( + data.decode("utf-8"), e + ) + ) elif "not well-formed (invalid token)" in e.msg: - xmppserverlog.debug('xml parse error - {} - {}'.format(data.decode('utf-8'), e)) + xmppserverlog.debug( + "xml parse error - {} - {}".format(data.decode("utf-8"), e) + ) else: - xmppserverlog.debug('xml parse error - {} - {}'.format(data.decode('utf-8'), e)) + xmppserverlog.debug( + "xml parse error - {} - {}".format(data.decode("utf-8"), e) + ) except Exception as e: - xmppserverlog.exception('{}'.format(e)) + xmppserverlog.exception("{}".format(e)) - def _handle_bind(self, xml): - try: + def _handle_bind(self, xml): + try: bumper_bots = self.bumper_bots.get() bumper_clients = self.bumper_clients.get() for bot in bumper_bots: if self.uid == bot.did: bot.xmpp_connection = True - #xmppserverlog.info("bot connected {}".format(bot.did)) + # xmppserverlog.info("bot connected {}".format(bot.did)) self.bumper_bots.set(bumper_bots) for client in bumper_clients: if self.uid == client.userid: client.xmpp_connection = True - #xmppserverlog.info("client connected {}".format(client.userid)) - self.bumper_clients.set(bumper_clients) + # xmppserverlog.info("client connected {}".format(client.userid)) + self.bumper_clients.set(bumper_clients) clientbindxml = xml.getchildren() clientresourcexml = clientbindxml[0].getchildren() if len(clientresourcexml) > 0: self.clientresource = clientresourcexml[0].text self.name = "XMPP_Client_{}".format(self.clientresource) - xmppserverlog.debug("new client {} using resource {}".format(self.address, self.clientresource)) - res = '{}@{}/{}'.format(xml.get('id'), self.uid, XMPPServer.bot_id, self.clientresource) + xmppserverlog.debug( + "new client {} using resource {}".format( + self.address, self.clientresource + ) + ) + res = '{}@{}/{}'.format( + xml.get("id"), self.uid, XMPPServer.bot_id, self.clientresource + ) else: xmppserverlog.debug("new client {}".format(self.address)) - res = '{}@{}'.format(xml.get('id'), self.uid, XMPPServer.bot_id) + res = '{}@{}'.format( + xml.get("id"), self.uid, XMPPServer.bot_id + ) - self._set_state('BIND') + self._set_state("BIND") self.send(res) - + except Exception as e: - xmppserverlog.exception('{}'.format(e)) + xmppserverlog.exception("{}".format(e)) def _handle_session(self, xml): - try: - res = ''.format(xml.get('id')) - self._set_state('READY') - self.send(res) - - except Exception as e: - xmppserverlog.exception('{}'.format(e)) - - def _handle_presence(self, xml): try: - if len(xml) and xml[0].tag == 'status': + res = ''.format(xml.get("id")) + self._set_state("READY") + self.send(res) + + except Exception as e: + xmppserverlog.exception("{}".format(e)) + + def _handle_presence(self, xml): + try: + if len(xml) and xml[0].tag == "status": # bot announcing arrival self.type = self.BOT - xmppserverlog.debug('{} type set to BOT (based on presence tag)'.format(self.address)) + xmppserverlog.debug( + "{} type set to BOT (based on presence tag)".format(self.address) + ) # send a command from an unknown user - the response will contain the correct admin username - - self.send(''.format(uuid.uuid4(), 'unknown@ecouser.net', XMPPServer.bot_id)) + + self.send( + ''.format( + uuid.uuid4(), "unknown@ecouser.net", XMPPServer.bot_id + ) + ) else: self.type = self.CONTROLLER - xmppserverlog.debug('{} type set to CONTROLLER (based on presence tag)'.format(self.address)) - self.send(' dummy '.format(self.uid, XMPPServer.bot_id, self.clientresource)) + xmppserverlog.debug( + "{} type set to CONTROLLER (based on presence tag)".format( + self.address + ) + ) + self.send( + ' dummy '.format( + self.uid, XMPPServer.bot_id, self.clientresource + ) + ) except Exception as e: - xmppserverlog.exception('{}'.format(e)) + xmppserverlog.exception("{}".format(e)) def _parse_data(self, data): if self.log_incoming_data: - xmppserverlog.debug('from {} - {}'.format(self.address, data.decode('utf-8'))) - + xmppserverlog.debug( + "from {} - {}".format(self.address, data.decode("utf-8")) + ) + try: - xml = ET.fromstring(data.decode('utf-8')) + xml = ET.fromstring(data.decode("utf-8")) self._handle_xml(xml, data) except ET.ParseError as e: - if "no element found" in e.msg: #Element not closed or not all bytes received - #Happens wth connect stream often - if ' - client is signalling end of session/disconnect - if not '' in data.decode('utf-8'): - xmppserverlog.error('xml parse error - {} - {}'.format(data.decode('utf-8'), e)) - else: - self.send("") #Close stream + if not (data.decode("utf-8") == "" or data.decode("utf-8") == " "): + xmppserverlog.error( + "xml parse error - {} - {}".format(data.decode("utf-8"), e) + ) + + elif "not well-formed (invalid token)" in e.msg: + # If a lone - client is signalling end of session/disconnect + if not "" in data.decode("utf-8"): + xmppserverlog.error( + "xml parse error - {} - {}".format(data.decode("utf-8"), e) + ) + else: + self.send("") # Close stream + + elif ( + "junk after document element" in e.msg + ): # More than one xml doc in data + # try to split it + data0 = data.decode("utf-8") + data1 = data0[e.position[1] :] + data0 = data0[: e.position[1]] + # xmppserverlog.debug('xml parse error - {} - {} - split0: {} - split1: {}'.format(data.decode('utf-8'), e, data0, data1)) + self._parse_data(data0.encode("utf-8")) + self._parse_data(data1.encode("utf-8")) - elif "junk after document element" in e.msg: #More than one xml doc in data - #try to split it - data0 = data.decode('utf-8') - data1 = data0[e.position[1]:] - data0 = data0[:e.position[1]] - #xmppserverlog.debug('xml parse error - {} - {} - split0: {} - split1: {}'.format(data.decode('utf-8'), e, data0, data1)) - self._parse_data(data0.encode('utf-8')) - self._parse_data(data1.encode('utf-8')) - else: - xmppserverlog.debug('xml parse error - {} - {}'.format(data.decode('utf-8'), e)) + xmppserverlog.debug( + "xml parse error - {} - {}".format(data.decode("utf-8"), e) + ) except Exception as e: - xmppserverlog.exception('{}'.format(e)) + xmppserverlog.exception("{}".format(e)) def _handle_xml(self, xml, data): try: if self.state == self.CONNECT or self.state == self.INIT: - self._handle_connect(data) + self._handle_connect(data) if len(xml): child = self._tag_strip_uri(xml[0].tag) else: child = None - if xml.tag == 'iq': - if child == 'bind': + if xml.tag == "iq": + if child == "bind": self._handle_bind(xml) - elif child == 'session': + elif child == "session": self._handle_session(xml) - elif child == 'query': + elif child == "query": self._handle_ctl(xml, data) - elif child == 'ping': + elif child == "ping": self._handle_ping(xml, data) - elif xml.get('type') == 'result': - self._handle_result(data) - elif xml.tag == 'presence': + elif xml.get("type") == "result": + self._handle_result(data) + elif xml.tag == "presence": self._handle_presence(xml) except Exception as e: - xmppserverlog.exception('{}'.format(e)) - + xmppserverlog.exception("{}".format(e)) + + def run(self): + # xmppserverlog.info('client connected - {}'.format(self.address)) + self._set_state("CONNECT") - def run(self): - #xmppserverlog.info('client connected - {}'.format(self.address)) - self._set_state('CONNECT') - while not self.state == self.DISCONNECT and not self.connection._closed: - data = b'' + data = b"" time.sleep(0.2) - if not self.connection._closed: - try: - data = self.connection.recv(4096) - + if not self.connection._closed: + try: + data = self.connection.recv(4096) + except ConnectionResetError as e: - xmppserverlog.error('{}'.format(e)) + xmppserverlog.error("{}".format(e)) except OSError as e: - xmppserverlog.error('{}'.format(e)) + xmppserverlog.error("{}".format(e)) except Exception as e: - xmppserverlog.exception('{}'.format(e)) - - if data != b'': + xmppserverlog.exception("{}".format(e)) + + if data != b"": self._parse_data(data) - \ No newline at end of file + diff --git a/examples/sucks.py b/examples/sucks.py index 812c074..3c890e4 100644 --- a/examples/sucks.py +++ b/examples/sucks.py @@ -2,19 +2,22 @@ from sucks import * + class BumperVacBot(VacBot): def __init__(self, server_address): self.server_address = server_address - vacuum = { 'did':'none','class':'none' } - super().__init__('sucks', 'ecouser.net', '', '', vacuum, '') + vacuum = {"did": "none", "class": "none"} + super().__init__("sucks", "ecouser.net", "", "", vacuum, "") + def connect_and_wait_until_ready(self): - logging.info('connecting') + logging.info("connecting") self.xmpp.connect(self.server_address) self.xmpp.process() self.xmpp.wait_until_ready() -logging.basicConfig(level=logging.DEBUG, format='%(levelname)-8s %(message)s') -server_address = ('xxx.xxx.xxx.xxx', 5223) + +logging.basicConfig(level=logging.DEBUG, format="%(levelname)-8s %(message)s") +server_address = ("xxx.xxx.xxx.xxx", 5223) # Initialize vacbot = BumperVacBot(server_address)