diff --git a/bumper.py b/bumper.py index 8dbf6dd..3a7bb61 100644 --- a/bumper.py +++ b/bumper.py @@ -4,14 +4,20 @@ import logging import bumper import sys, socket -logging.basicConfig(level=logging.INFO, +logging.basicConfig(level=logging.DEBUG, format='%(asctime)s %(levelname)-8s %(message)s') -conf_address = (socket.gethostbyname(socket.gethostname()), 8007) -xmpp_address = (socket.gethostbyname(socket.gethostname()), 5223) +#conf_address = (socket.gethostbyname(socket.gethostname()), 443) +conf_address = ("0.0.0.0", 443) +#xmpp_address = (socket.gethostbyname(socket.gethostname()), 5223) +xmpp_address = ("0.0.0.0", 5223) +#mqtt_address = (socket.gethostbyname(socket.gethostname()), 8883) +mqtt_address = ("0.0.0.0", 8883) # start conf server (async) -conf_server = bumper.ConfServer(conf_address, ssl=False, async=True) +conf_server = bumper.ConfServer(conf_address, usessl=True, run_async=True) +# start mqtt server (async) +mqtt_server = bumper.MQTTServer(mqtt_address, run_async=True) # start xmpp server (sync) xmpp_server = bumper.XMPPServer(xmpp_address) diff --git a/bumper/__init__.py b/bumper/__init__.py index 6ff2fd5..e75e9aa 100644 --- a/bumper/__init__.py +++ b/bumper/__init__.py @@ -1,4 +1,5 @@ #!/usr/bin/env python3 from .confserver import ConfServer +from .mqttserver import MQTTServer from .xmppserver import XMPPServer diff --git a/bumper/confserver.py b/bumper/confserver.py index dd31f8f..bf72faf 100644 --- a/bumper/confserver.py +++ b/bumper/confserver.py @@ -2,12 +2,14 @@ from http.server import HTTPServer from http.server import BaseHTTPRequestHandler +#from http.server import SimpleHTTPRequestHandler from http import HTTPStatus from threading import Thread import socket, logging, ssl, json, sys class RequestHandler(BaseHTTPRequestHandler): +#class RequestHandler(SimpleHTTPRequestHandler): def do_POST(self): try: self.protocol_version = 'HTTP/1.1' @@ -17,17 +19,44 @@ class RequestHandler(BaseHTTPRequestHandler): request_body = post_data.decode('utf-8') logging.debug("Request: " + request_body) json_body = json.loads(request_body) - todo = json_body['todo'] - if todo == 'FindBest': - service = json_body['service'] - if service == 'EcoMsgNew': - body = '{{"result":"ok","ip":"{}","port":5223}}'.format(socket.gethostbyname(socket.gethostname())) - elif service == 'EcoUpdate': - body = '{"result":"ok","ip":"47.88.66.164","port":8005}' - elif todo == 'loginByItToken': - body = "{{'todo': 'result', 'result': 'ok', 'userId': '{}', 'resource': '{}', 'token': '{}'}}".format(json_body['userId'], json_body['resource'], json_body['token']) - elif todo == 'GetDeviceList': - body = "{'todo': 'result', 'result': 'ok', 'devices': [{'did': '{}', 'name': '{}', 'class': '{}', 'resource': 'atom', 'nick': None, 'company': 'eco'}]}" + if "getProductIotMap" in str(self.path): + body = {"code":0,"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"}}]} + elif "notify_engine.do" in str(self.path): + body = {"ret":"ok"} + else: + todo = json_body['todo'] + if todo == 'FindBest': + service = json_body['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': + body = { + "todo": "result", + "result": "ok", + "userId": json_body["userId"], + "resource": json_body["resource"], + "token": json_body["token"] + } + elif todo == 'GetDeviceList': + #Find a way to handle this automatically + #Maybe keep a list of devices from those that have checked into MQTT/XMPP/etc and return them + body = { + "devices": [ + { + "class": "tmpClass", + "company": "eco-ng", + "did": "tmpDeviceID", + "name": "tmpName", + "nick": "tmpNick", + "resource": "tmpResource" + } + ], + "result": "ok", + "todo": "result" + } + body = json.dumps(body) logging.debug("Response: " + body) body = body.encode() self.send_response(HTTPStatus.OK) @@ -37,7 +66,73 @@ class RequestHandler(BaseHTTPRequestHandler): self.end_headers() self.wfile.write(body) except Exception as e: - logging.error('ConfServer: {}'.format(e)) + logging.error('ConfServer POST: {}'.format(e)) + + def do_GET(self): + try: + self.protocol_version = 'HTTP/1.1' + logging.debug("Path: " + self.path) + + if "/login?" in str(self.path): + + #Could implement basic auth if you wanted, or just accept anything + #Next up parse the path for variables + body = { + "code": "0000", + "data": { + "accessToken": "temp_accesstoken", + "country": "us", + "email": "null@null.com", + "uid": "fuid_1", + "username": "fusername_1" + }, + "msg": "操作成功", + "time": 1547211900690 + } + + + elif "/getAuthCode?" in str(self.path): + body = { + "code": "0000", + "data": { + "authCode": "temp_authcode", + "ecovacsUid": "fuid_1" + }, + "msg": "操作成功", + "time": 1547211900808 + } + + elif "/checkVersion?" in str(self.path): + body = { + "code": "0000", + "data": { + "c": None, + "img": None, + "r": 0, + "t": None, + "u": None, + "ut": 0, + "v": None + }, + "msg": "操作成功", + "time": 1548829850462 + } + elif "/logout?" in str(self.path): + body = {"code": "0000","data": None,"msg": "操作成功","time": 1548830041460} + else: + body = {} + + body = json.dumps(body) + logging.debug("Response: " + body) + body = body.encode() + self.send_response(HTTPStatus.OK) + self.send_header('Content-Type', 'application/json; charset=utf-8') + self.send_header('Connection', 'keep-alive') + self.send_header('Content-Length', len(body)) + self.end_headers() + self.wfile.write(body) + except Exception as e: + logging.error('ConfServer GET: {}'.format(e)) class HTTPServerThread(HTTPServer, Thread): @@ -46,11 +141,12 @@ class HTTPServerThread(HTTPServer, Thread): self.server_address = server_address self.handler = RequestHandler self.exit_flag = False + HTTPServer.__init__(self, self.server_address, self.handler) + def handle_error(self, request, client_address): self.close_request(request) def run(self): - try: - HTTPServer.__init__(self, self.server_address, self.handler) + try: logging.info('ConfServer: listening on {}:{}'.format(self.server_address[0], self.server_address[1])) while not self.exit_flag: self.handle_request() @@ -65,13 +161,13 @@ class HTTPServerThread(HTTPServer, Thread): class ConfServer(): - def __init__(self, address, ssl=False, async=True): - try: - self.async = async - self.server = HTTPServerThread(address) - if ssl: + def __init__(self, address, usessl=False, run_async=True): + self.run_async = run_async + self.server = HTTPServerThread(address) + try: + if usessl: self.server.socket = ssl.wrap_socket(self.server.socket, keyfile='./certs/key.pem', certfile='./certs/cert.pem', server_side=True) - if self.async: + if self.run_async: self.server.start() else: try: @@ -83,6 +179,6 @@ class ConfServer(): def disconnect(self): logging.info('ConfServer: shutting down...') self.server.disconnect() - if(self.async): + if(self.run_async): self.server.join() logging.info('ConfServer: bye') diff --git a/bumper/mqttserver.py b/bumper/mqttserver.py index 34b6c84..441c19e 100644 --- a/bumper/mqttserver.py +++ b/bumper/mqttserver.py @@ -6,87 +6,52 @@ import os from hbmqtt.broker import Broker import hbmqtt -default_config = { - 'listeners': { - 'default': { - 'type': 'tcp', - 'bind': '0.0.0.0:1883', - }, - 'tls1': { - 'bind': '0.0.0.0:8883', - 'ssl': 'on', - #'cafile': '/some/cafile', - #'capath': '/some/folder', - #'capath': 'certificate data', - 'certfile': './certs/cert.pem', - 'keyfile': './certs/key.pem', - }, - }, - 'sys_interval': 10, - 'auth': { - 'allow-anonymous': True, - 'password-file': os.path.join(os.path.dirname(os.path.realpath(__file__)), "passwd"), - 'plugins': [ - 'VacBotAuth' - ] - }, - 'topic-check': { - 'enabled': False - } -} - -class BaseAuthPlugin: - def __init__(self, context): - self.context = context - try: - self.auth_config = self.context.config['auth'] - except KeyError: - self.context.logger.warning("'auth' section not found in context configuration") - - 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") - return False - return True - -class VacBotAuth(BaseAuthPlugin): - def __init__(self, context): - super().__init__(context) +class MQTTServer(): + clients = [] + exit_flag = False + default_config = {} @asyncio.coroutine - def authenticate(self, *args, **kwargs): - authenticated = super().authenticate(*args, **kwargs) - if authenticated: - 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: - session = kwargs.get('session', None) - authenticated = True if session.username else False - if self.context.logger.isEnabledFor(logging.DEBUG): - if authenticated: - self.context.logger.debug("Authentication success: session has a non empty username") - else: - self.context.logger.debug("Authentication failure: session has an empty username") - except KeyError: - self.context.logger.warning("Session informations not available") - authenticated = False - return authenticated - -@asyncio.coroutine -def broker_coro(): - broker = hbmqtt.broker.Broker(config=default_config, plugin_namespace=".") + def broker_coro(self): + broker = hbmqtt.broker.Broker(config=self.default_config, plugin_namespace=".") - print(broker.plugins_manager) - yield from broker.start() + yield from broker.start() + def __init__(self, address, run_async=False): + try: + # Initialize bot server + self.default_config = { + 'listeners': { + 'default': { + 'type': 'tcp', + }, + 'tls1': { + 'bind': "{}:{}".format(address[0], address[1]), + 'ssl': 'on', + 'certfile': './certs/cert.pem', + 'keyfile': './certs/key.pem', + }, + }, + 'sys_interval': 10, + 'auth': { + 'allow-anonymous': True, + 'password-file': os.path.join(os.path.dirname(os.path.realpath(__file__)), "passwd"), + 'plugins': [ + 'VacBotAuth' + ] + }, + 'topic-check': { + 'enabled': False + } + } + if run_async: + self.run() + finally: + print("Done") + + def run(self): + formatter = "[%(asctime)s] :: %(levelname)s :: %(name)s :: %(message)s" + logging.basicConfig(level=logging.DEBUG, format=formatter) + eloop = asyncio.get_event_loop().run_until_complete(self.broker_coro()) + asyncio.get_event_loop().run_forever() -if __name__ == '__main__': - formatter = "[%(asctime)s] :: %(levelname)s :: %(name)s :: %(message)s" - logging.basicConfig(level=logging.DEBUG, format=formatter) - eloop = asyncio.get_event_loop().run_until_complete(broker_coro()) - - asyncio.get_event_loop().run_forever() \ No newline at end of file