diff --git a/bumper.py b/bumper.py index be48215..c646c59 100644 --- a/bumper.py +++ b/bumper.py @@ -72,11 +72,11 @@ 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: - bumper.bumperlog.info("Bumper Exiting - Keyboard Interrupt") + bumper.bumperlog.info("Bumper Exiting - Keyboard Interrupt") print("Bumper Exiting") exit(1) diff --git a/bumper/__init__.py b/bumper/__init__.py index 6ae059c..5e260f2 100644 --- a/bumper/__init__.py +++ b/bumper/__init__.py @@ -70,7 +70,7 @@ class BumperUser(object): def revoke_authcode(self, authcode): if authcode in self.authcodes: - self.authcodes.remove(authcode) + self.authcodes.remove(authcode) def add_bot(self, botdid): if not botdid in self.bots: @@ -78,7 +78,7 @@ class BumperUser(object): def remove_bot(self, botdid): if botdid in self.bots: - self.bots.remove(botdid) + self.bots.remove(botdid) class VacBotDevice(object): def __init__(self,did="", vac_bot_device_class="",resource="" , name="", nick="", company="eco-ng"): @@ -128,7 +128,7 @@ 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) @@ -141,7 +141,7 @@ def add_client(userid, realm, resource): newclient.resource = resource clients = bumper_clients_var.get() - + existingclient = False for client in clients: if client.userid == newclient.userid: diff --git a/bumper/confserver.py b/bumper/confserver.py index 8f8d5db..c3f76ba 100644 --- a/bumper/confserver.py +++ b/bumper/confserver.py @@ -17,7 +17,7 @@ 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 record.levelno = 10 - record.levelname = "DEBUG" + record.levelname = "DEBUG" if record.levelno == 10 and logging.getLogger("confserver").getEffectiveLevel() == 10: return True @@ -44,13 +44,13 @@ class ConfServer(): def run(self, run_async=False): - try: + 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.setDaemon(True) self.confthread.start() - + else: try: self.run_server() @@ -73,19 +73,19 @@ class ConfServer(): 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.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/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/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), @@ -94,22 +94,20 @@ class ConfServer(): 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: site = web.TCPSite(runner, host=self.address[0], port=self.address[1]) @@ -121,18 +119,18 @@ class ConfServer(): 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)) + confserverlog.exception('{}'.format(e)) async def handle_login(self, request): try: @@ -141,10 +139,10 @@ class ConfServer(): 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() + users = self.bumper_users.get() for user in users: if user_devid in user.devices: - tmpaccesstoken = '' + 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'] @@ -166,12 +164,12 @@ class ConfServer(): "data": None, "msg": "当前密码错误", "time": bumper.get_milli_time(time.time()) - } + } else: if tmpaccesstoken == '': tmpaccesstoken = uuid.uuid4().hex user.add_token(tmpaccesstoken) - + body = { "code": bumper.RETURN_API_SUCCESS, "data": { @@ -184,38 +182,38 @@ class ConfServer(): "msg": "操作成功", "time": bumper.get_milli_time(time.time()) } - self.bumper_users.set(users) - + 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()) - } - + } + return web.json_response(body) - else: - return web.json_response(self._auth_any(user_devid, countrycode, request)) + else: + return web.json_response(self._auth_any(user_devid, countrycode, request)) except Exception as e: - confserverlog.exception('{}'.format(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: + else: tmpuser = bumper.BumperUser('tmpuser') users.append(tmpuser) tmpuser.add_device(user_devid) @@ -223,7 +221,7 @@ class ConfServer(): for bot in bots: tmpuser.add_bot(bot.did) - if 'checkLogin' in request.path: + if 'checkLogin' in request.path: tmpaccesstoken = request.query['accessToken'] tmpuser.add_token(tmpaccesstoken) body = { @@ -242,7 +240,7 @@ class ConfServer(): if tmpaccesstoken == '': tmpaccesstoken = uuid.uuid4().hex tmpuser.add_token(tmpaccesstoken) - + body = { "code": bumper.RETURN_API_SUCCESS, "data": { @@ -255,44 +253,44 @@ class ConfServer(): "msg": "操作成功", "time": bumper.get_milli_time(time.time()) } - self.bumper_users.set(users) - + 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): - try: + async def handle_logout(self, request): + try: 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: + 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())} - + self.bumper_users.set(users) + + body = {"code": bumper.RETURN_API_SUCCESS,"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_getAuthCode(self, request): + async def handle_getAuthCode(self, request): try: - + 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: + 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": { @@ -310,14 +308,14 @@ class ConfServer(): "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, @@ -333,31 +331,31 @@ class ConfServer(): "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_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) 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()) - + body = { "code": bumper.RETURN_API_SUCCESS, "data": { @@ -371,19 +369,19 @@ class ConfServer(): "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_getProductIotMap(self, request): + 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)) + confserverlog.exception('{}'.format(e)) async def handle_usersapi(self, request): try: @@ -392,10 +390,10 @@ class ConfServer(): postbody = {} if request.content_type == "application/x-www-form-urlencoded": postbody = await request.post() - + else: - postbody = json.loads(await request.text()) - + postbody = json.loads(await request.text()) + todo = postbody['todo'] if todo == 'FindBest': service = postbody['service'] @@ -404,10 +402,10 @@ class ConfServer(): 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", @@ -425,20 +423,20 @@ class ConfServer(): "devices": bot_list, "result": "ok", "todo": "result" - } + } elif todo == 'SetDeviceNick': bots = self.bumper_bots.get() for bot in bots: if postbody['did'] == bot.did: bot.nick = postbody['nick'] - self.bumper_bots.set(bots) + self.bumper_bots.set(bots) body = { "result": "ok", "todo": "result", } - confserverlog.debug("\r\n POST: {} \r\n Response: {}".format(postbody,body)) + confserverlog.debug("\r\n POST: {} \r\n Response: {}".format(postbody,body)) return web.json_response(body) except Exception as e: @@ -451,12 +449,12 @@ class ConfServer(): 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'] @@ -464,23 +462,23 @@ class ConfServer(): 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)) + + 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)) + json_body = json.loads(await request.text()) + 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: retcmd = await self.helperbot.send_command(json_body, randomid) body = retcmd - confserverlog.debug("\r\n POST: {} \r\n Response: {}".format(json_body,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'])) diff --git a/bumper/mqttserver.py b/bumper/mqttserver.py index 91e733c..11bd4d0 100644 --- a/bumper/mqttserver.py +++ b/bumper/mqttserver.py @@ -37,12 +37,12 @@ class MQTTHelperBot(): def run(self, run_async=False): if run_async: - hloop = asyncio.new_event_loop() + 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() - + self.helperthread.start() + else: self.run_helperbot() @@ -51,13 +51,13 @@ class MQTTHelperBot(): logging.info("Starting MQTT HelperBot") print("Starting MQTT HelperBot") try: - asyncio.set_event_loop(loop) + 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)) + helperbotlog.exception('{}'.format(e)) async def start_helper_bot(self): @@ -65,41 +65,41 @@ class MQTTHelperBot(): 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) + ('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() - + #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"))}) - + 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) + 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)) - async def wait_for_resp(self, requestid): + 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() @@ -110,7 +110,7 @@ class MQTTHelperBot(): #helperbotlog.debug('VacBot MQTT Response: Topic: %s Payload: %s' % (msg['topic'], msg['payload'])) if topic[11] == "j": resppayload = json.loads(msg['payload']) - else: + else: resppayload = str(msg['payload']) resp = { "id": requestid, @@ -118,28 +118,28 @@ class MQTTHelperBot(): "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" } except asyncio.CancelledError as e: 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: + 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 except Exception as e: @@ -156,7 +156,7 @@ class MQTTServer(): 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: @@ -174,7 +174,7 @@ class MQTTServer(): 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): try: @@ -186,7 +186,7 @@ class MQTTServer(): #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) @@ -218,22 +218,22 @@ class MQTTServer(): '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,): if run_async: - sloop = asyncio.new_event_loop() + 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() - + self.mqttserverthread.start() + else: - self.run_server() + self.run_server() def run_server(self, loop): @@ -242,16 +242,16 @@ class MQTTServer(): try: asyncio.set_event_loop(loop) loop.run_until_complete(self.broker_coro()) - #loop.run_until_complete(self.active_bot_listing()) + #loop.run_until_complete(self.active_bot_listing()) loop.run_forever() except Exception as e: - mqttserverlog.exception('{}'.format(e)) + mqttserverlog.exception('{}'.format(e)) class BumperMQTTServer_Plugin: def __init__(self, context): self.context = context - try: + try: self.bumper_config = self.context.config['bumper'] self.auth_config = self.context.config['auth'] @@ -266,7 +266,7 @@ class BumperMQTTServer_Plugin: # auth config section not found 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 if allow_anonymous: authenticated = True @@ -280,21 +280,21 @@ class BumperMQTTServer_Plugin: 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])) + mqttserverlog.debug("new bot authenticated SN: {} DID: {}".format(username, didsplit[0])) authenticated = True - + else: - tmpclientdetail = str(didsplit[1]).split("/") + tmpclientdetail = str(didsplit[1]).split("/") userid = didsplit[0] realm = tmpclientdetail[0] resource = tmpclientdetail[1] - + if userid == "helper1": authenticated = True else: @@ -306,17 +306,17 @@ class BumperMQTTServer_Plugin: if auth: bumper.add_client(userid, realm, resource) - mqttserverlog.debug("client authenticated {}".format(userid)) + mqttserverlog.debug("client authenticated {}".format(userid)) authenticated = True - + else: authenticated = False - - except KeyError: - self.context.logger.warning("Session information not available") - authenticated = False - return authenticated + except Exception as e: + mqttserverlog.exception('{}'.format(e)) + authenticated = False + + return authenticated async def on_broker_client_connected(self, client_id): try: @@ -324,40 +324,40 @@ class BumperMQTTServer_Plugin: 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.info("bot connected {}".format(bot.did)) + mqttserverlog.debug("bot connected {}".format(bot.did)) self.bumper_config['bumper_bots'].set(bumper_bots) for client in bumper_clients: 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: + 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() didsplit = str(client_id).split("@") - + for bot in bumper_bots: if didsplit[0] == bot.did: bot.mqtt_connection = False - #mqttserverlog.info("bot disconnected {}".format(bot.did)) + mqttserverlog.debug("bot disconnected {}".format(bot.did)) self.bumper_config['bumper_bots'].set(bumper_bots) for client in bumper_clients: if didsplit[0] == client.userid and client.userid != 'helper1': client.mqtt_connection = False - #mqttserverlog.info("client disconnected {}".format(client.userid)) + #mqttserverlog.info("client disconnected {}".format(client.userid)) self.bumper_config['bumper_clients'].set(bumper_clients) except Exception as e: diff --git a/bumper/xmppserver.py b/bumper/xmppserver.py index a71b7c9..e0a9aa7 100644 --- a/bumper/xmppserver.py +++ b/bumper/xmppserver.py @@ -27,10 +27,10 @@ class XMPPServer(): 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 = Thread(name="XMPPServer_Thread",target=self.run_server) self.xmppthread.setDaemon(True) self.xmppthread.start() - + else: try: self.run_server() @@ -44,39 +44,39 @@ class XMPPServer(): #xmppserverlog.setLevel(logging.DEBUG) #Set SSL Context - self.ssl_ctx = ssl.create_default_context(ssl.Purpose.CLIENT_AUTH) + 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 = 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: + 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]: + if client.address == client_address[0]: xmppserverlog.debug('disconnecting existing client {} with resource {}'.format(client.address, client.clientresource)) client._disconnect() - self.remove_client_byip(client.address) - + self.remove_client_byip(client.address) + xmppserverlog.debug('starting new client with ip {}'.format(client_address[0])) - thread_id = uuid.uuid4() + 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) - + except PermissionError as e: if "bind" in e.strerror: xmppserverlog.exception("Error binding XMPPServer, exiting. Try using a different hostname or IP - {}".format(e)) - exit(1) - + exit(1) + except Exception as e: xmppserverlog.exception('{}'.format(e)) exit(1) @@ -88,16 +88,16 @@ class XMPPServer(): 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') for client in self.clients: client._disconnect() - + self.exit_flag = True xmppserverlog.debug('shutting down') @@ -172,11 +172,11 @@ class Client(threading.Thread): except ConnectionResetError as e: xmppserverlog.error('{}'.format(e)) - #self._set_state('DISCONNECT') + #self._set_state('DISCONNECT') except Exception as e: xmppserverlog.exception("{}".format(e)) - + def _disconnect(self): try: @@ -186,14 +186,14 @@ class Client(threading.Thread): if self.uid == bot.did: bot.xmpp_connection = False #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': 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)) self.connection.close() @@ -215,29 +215,29 @@ class Client(threading.Thread): new_state = getattr(Client, state) if self.state > new_state: raise Exception('{} illegal state change {}->{}'.format(self.address, self.state, new_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)) + 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 + 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)) - + else: xmppserverlog.debug('Unknown set type: {}'.format(data.decode('utf-8'))) @@ -246,7 +246,7 @@ class Client(threading.Thread): 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 + return #forward for client in XMPPServer.clients: @@ -275,9 +275,9 @@ class Client(threading.Thread): client.send(data.decode('utf-8')) except Exception as e: - xmppserverlog.exception('{}'.format(e)) + xmppserverlog.exception('{}'.format(e)) - def _handle_result(self, data): + def _handle_result(self, data): # forward try: for client in XMPPServer.clients: @@ -286,11 +286,11 @@ class Client(threading.Thread): except Exception as e: xmppserverlog.exception("{}".format(e)) - + - def _handle_connect(self, data): + def _handle_connect(self, data): try: - + if self.state == self.CONNECT: #Client first connecting, send our features @@ -302,15 +302,15 @@ class Client(threading.Thread): #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 self._handle_iq_auth(data) 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: @@ -318,34 +318,34 @@ class Client(threading.Thread): 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')) + 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': self._handle_bind(xml) - + except Exception as 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] + 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'))) + 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: + if xml.get('type') == 'set' and "auth}username" in ctl.tag and self.type == self.UNKNOWN: xmlauth = xml[0].getchildren() uid = '' password = '' @@ -353,7 +353,7 @@ class Client(threading.Thread): for aitem in xmlauth: if 'username' in aitem.tag: self.uid = aitem.text - + elif 'password' in aitem.tag: password = aitem.text.split("/")[2] authcode = password @@ -363,18 +363,18 @@ class Client(threading.Thread): resource = self.clientresource if not self.uid.startswith("fuid"): - + #Need sample data to see details here - bumper.add_bot('',self.uid, '', resource) + bumper.add_bot('',self.uid, '', resource) xmppserverlog.info("bot authenticated {}".format(self.uid)) - - #Client authenticated, move to next state + + #Client authenticated, move to next state self._set_state('INIT') - + #Successful auth - self.send(''.format(xml.get('id'))) - - else: + self.send(''.format(xml.get('id'))) + + else: auth = False if bumper.check_authcode(self.uid, authcode): auth = True @@ -382,34 +382,34 @@ class Client(threading.Thread): auth = True if auth: - bumper.add_client(self.uid, 'bumper', self.clientresource) - xmppserverlog.debug("client authenticated {}".format(self.uid)) - - #Client authenticated, move to next state + 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') - + #Successful auth self.send(''.format(xml.get('id'))) - + else: #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)) - def _handle_sasl_auth(self, data): + def _handle_sasl_auth(self, data): try: - xml = ET.fromstring(data.decode('utf-8')) + 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] @@ -420,15 +420,15 @@ class Client(threading.Thread): if not self.uid.startswith("fuid"): #Need sample data to see details here - bumper.add_bot('',self.uid, '', resource) + bumper.add_bot('',self.uid, '', resource) xmppserverlog.info("bot authenticated {}".format(self.uid)) #Send response self.send('') #Success - #Client authenticated, move to next state + #Client authenticated, move to next state self._set_state('INIT') - - else: + + else: auth = False if bumper.check_authcode(self.uid, authcode): auth = True @@ -436,35 +436,35 @@ class Client(threading.Thread): auth = True if auth: - bumper.add_client(self.uid, 'bumper', self.clientresource) - xmppserverlog.debug("client authenticated {}".format(self.uid)) - - #Client authenticated, move to next state + 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 - + else: #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): + 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 @@ -474,7 +474,7 @@ class Client(threading.Thread): for client in bumper_clients: if self.uid == client.userid: client.xmpp_connection = True - #xmppserverlog.info("client connected {}".format(client.userid)) + #xmppserverlog.info("client connected {}".format(client.userid)) self.bumper_clients.set(bumper_clients) clientbindxml = xml.getchildren() @@ -487,15 +487,15 @@ class Client(threading.Thread): else: xmppserverlog.debug("new client {}".format(self.address)) res = '{}'.format(xml.get('id'), XMPPServer.bot_id) - + 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: + try: res = ''.format(xml.get('id')) self._set_state('READY') self.send(res) @@ -503,59 +503,60 @@ class Client(threading.Thread): except Exception as e: xmppserverlog.exception('{}'.format(e)) - def _handle_presence(self, xml): + 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)) # 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)) - + 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)) - + 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'))) - try: + try: 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 + 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 + 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]:] + 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)) @@ -563,29 +564,29 @@ class Client(threading.Thread): 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 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': 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): @@ -601,12 +602,12 @@ class Client(threading.Thread): 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'': + + if data != b'': self._parse_data(data) - \ No newline at end of file + \ No newline at end of file