diff --git a/.gitignore b/.gitignore index 3595135..ebef324 100644 --- a/.gitignore +++ b/.gitignore @@ -5,4 +5,5 @@ __pycache__ .noseids nosetests.xml tests/report -tests/tmp.db \ No newline at end of file +tests/tmp.db +logs/ \ No newline at end of file diff --git a/bumper/__init__.py b/bumper/__init__.py index 8501819..6e53947 100644 --- a/bumper/__init__.py +++ b/bumper/__init__.py @@ -5,21 +5,18 @@ from .mqttserver import MQTTServer from .mqttserver import MQTTHelperBot from .xmppserver import XMPPServer import asyncio -import contextvars import json import time from datetime import datetime, timedelta import platform -import os +import os, sys import logging +from logging.handlers import RotatingFileHandler from base64 import b64decode, b64encode from tinydb import TinyDB, Query +import json from tinydb.storages import MemoryStorage -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" @@ -29,17 +26,42 @@ token_validity_seconds = 3600 # 1 hour db = None # Logs +os.makedirs("logs", exist_ok=True) #Ensure logs directory exists or create +# Set format for all logs +logformat = logging.Formatter("[%(asctime)s] :: %(levelname)s :: %(name)s :: %(module)s :: %(funcName)s :: %(lineno)d :: %(message)s") + bumperlog = logging.getLogger("bumper") +bumper_rotate = RotatingFileHandler("logs/bumper.log", maxBytes=5000000, backupCount=5) +bumper_rotate.setFormatter(logformat) +bumperlog.addHandler(bumper_rotate) +# Override the logging level +# bumperlog.setLevel(logging.INFO) + confserverlog = logging.getLogger("confserver") +conf_rotate = RotatingFileHandler("logs/confserver.log", maxBytes=5000000, backupCount=5) +conf_rotate.setFormatter(logformat) +confserverlog.addHandler(conf_rotate) # Override the logging level # confserverlog.setLevel(logging.INFO) + mqttserverlog = logging.getLogger("mqttserver") +mqtt_rotate = RotatingFileHandler("logs/mqttserver.log", maxBytes=5000000, backupCount=5) +mqtt_rotate.setFormatter(logformat) +mqttserverlog.addHandler(mqtt_rotate) # Override the logging level # mqttserverlog.setLevel(logging.INFO) + helperbotlog = logging.getLogger("helperbot") +helperbot_rotate = RotatingFileHandler("logs/helperbot.log", maxBytes=5000000, backupCount=5) +helperbot_rotate.setFormatter(logformat) +helperbotlog.addHandler(helperbot_rotate) # Override the logging level # helperbotlog.setLevel(logging.INFO) + xmppserverlog = logging.getLogger("xmppserver") +xmpp_rotate = RotatingFileHandler("logs/xmppserver.log", maxBytes=5000000, backupCount=5) +xmpp_rotate.setFormatter(logformat) +xmppserverlog.addHandler(xmpp_rotate) # Override the logging level # xmppserverlog.setLevel(logging.INFO) @@ -56,23 +78,32 @@ def db_file(): def os_db_path(): - if platform.system() == "Windows": - return os.path.join(os.getenv("APPDATA"), "bumper.db") + if platform.system() == "Windows": + os.makedirs(os.getenv("APPDATA"), exist_ok=True) #Ensure db_path directory exists or create + return os.path.join(os.getenv("APPDATA"), "bumper.db") else: + os.makedirs(os.path.expanduser("~/.config"), exist_ok=True) #Ensure db_path directory exists or create return os.path.expanduser("~/.config/bumper.db") - def db_get(): - # Will create the database if it doesn't exist - db = TinyDB(db_file()) + try: + # Will create the database if it doesn't exist + db = TinyDB(db_file()) - # Will create the tables if they don't exist - db.table("users", cache_size=0) - db.table("clients", cache_size=0) - db.table("bots", cache_size=0) - db.table("tokens", cache_size=0) + # Will create the tables if they don't exist + db.table("users", cache_size=0) + db.table("clients", cache_size=0) + db.table("bots", cache_size=0) + db.table("tokens", cache_size=0) - return db + return db + + + except json.decoder.JSONDecodeError as jerr: + bumperlog.error("JsonErr: {} - Doc: {}".format(jerr.msg, jerr.doc)) + + except Exception as ex: + bumperlog.error(ex) class BumperUser(object): diff --git a/bumper/confserver.py b/bumper/confserver.py index beb25da..07c8590 100644 --- a/bumper/confserver.py +++ b/bumper/confserver.py @@ -8,7 +8,6 @@ import bumper import time from datetime import datetime, timedelta import asyncio -import contextvars from aiohttp import web import uuid import xml.etree.ElementTree as ET @@ -33,7 +32,7 @@ class aiohttp_filter(logging.Filter): 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 EcoVacs_Login: diff --git a/bumper/mqttserver.py b/bumper/mqttserver.py index 115e71d..0458787 100644 --- a/bumper/mqttserver.py +++ b/bumper/mqttserver.py @@ -8,7 +8,6 @@ from hbmqtt.broker import Broker from hbmqtt.client import MQTTClient from hbmqtt.mqtt.constants import QOS_0, QOS_1, QOS_2 import pkg_resources -import contextvars import time from threading import Thread import ssl @@ -40,7 +39,7 @@ class MQTTHelperBot: ): self.address = address self.client_id = "helper1@bumper/helper1" - self.command_responses = contextvars.ContextVar("command_responses", default=[]) + self.command_responses = [] self.helperthread = None def run(self, run_async=False): @@ -73,6 +72,10 @@ class MQTTHelperBot: async def start_helper_bot(self): try: + self.Client = MQTTClient( + client_id=self.client_id, config={"check_hostname": False} + ) + await self.Client.connect( "mqtts://{}:{}/".format(self.address[0], self.address[1]), cafile=bumper.ca_cert, @@ -81,9 +84,12 @@ class MQTTHelperBot: [ ("iot/p2p/+/+/+/+/helper1/bumper/helper1/+/+/+", QOS_0), ("iot/p2p/+", QOS_0), + ("iot/atr/+", QOS_0), ] ) + asyncio.ensure_future(self.get_msg()) + except Exception as e: helperbotlog.exception("{}".format(e)) @@ -92,28 +98,34 @@ class MQTTHelperBot: 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( + #Response to command + helperbotlog.debug("Received Response - Topic: {} - Message: {}".format(message.topic, str(message.data.decode("utf-8")))) + self.command_responses.append( { "time": time.time(), "topic": message.topic, "payload": str(message.data.decode("utf-8")), } ) + elif str(message.topic).split("/")[3] == "helper1": + #Helperbot sending command + helperbotlog.debug("Send Command - Topic: {} - Message: {}".format(message.topic, str(message.data.decode("utf-8")))) + elif str(message.topic).split("/")[1] == "atr": + #Broadcast message received on atr + helperbotlog.debug("Received Broadcast - Topic: {} - Message: {}".format(message.topic, str(message.data.decode("utf-8")))) + else: + helperbotlog.debug("Received Message - Topic: {} - Message: {}".format(message.topic, str(message.data.decode("utf-8")))) # Cleanup "expired messages" > 60 seconds from time - for msg in cresp: + for msg in self.command_responses: 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) + helperbotlog.debug("Pruning Message Time: {}, MsgTime: {}, MsgTime+60: {}".format(time.time(), msg['time'], expire_time)) + self.command_responses.remove(msg) - self.command_responses.set(cresp) # helperbotlog.debug("MQTT Command Response List Count: %s" %len(cresp)) except Exception as e: @@ -125,10 +137,9 @@ class MQTTHelperBot: 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: - for msg in responses: + await asyncio.sleep(0.1) + if len(self.command_responses) > 0: + for msg in self.command_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'])) @@ -136,17 +147,17 @@ class MQTTHelperBot: 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) + resp = {"id": requestid, "ret": "ok", "resp": resppayload} + self.command_responses.remove(msg) return resp return {"id": requestid, "errno": "timeout", "ret": "fail"} except asyncio.CancelledError as e: helperbotlog.debug("wait_for_resp cancelled by asyncio") + return {"id": requestid, "errno": "timeout", "ret": "fail"} except Exception as e: helperbotlog.exception("{}".format(e)) + return {"id": requestid, "errno": "timeout", "ret": "fail"} async def send_command(self, cmdjson, requestid): try: @@ -171,6 +182,7 @@ class MQTTHelperBot: except Exception as e: helperbotlog.exception("{}".format(e)) + return {} class MQTTServer: diff --git a/bumper/xmpp_old_client.py b/bumper/xmpp_old_client.py new file mode 100644 index 0000000..ea5b24b --- /dev/null +++ b/bumper/xmpp_old_client.py @@ -0,0 +1,769 @@ +class Client(threading.Thread): + IDLE = 0 + CONNECT = 1 + INIT = 2 + BIND = 3 + READY = 4 + DISCONNECT = 5 + UNKNOWN = 0 + BOT = 1 + CONTROLLER = 2 + + def __init__(self, thread_id, connection, client_address): + threading.Thread.__init__(self) + self.id = thread_id + self.name = "XMPP_Client_{}".format(client_address[0]) + self.type = self.UNKNOWN + self.state = self.IDLE + self.connection = connection + self.address = client_address[0] + self.clientresource = "" + self.devclass = "" + self.bumper_jid = "" + self.uid = "" + self.log_sent_message = False # Set to true to log sends + self.log_incoming_data = True # Set to true to log sends + + 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()) + + except BrokenPipeError as e: + xmppserverlog.debug("{}".format(e)) + self._set_state("DISCONNECT") + + except ConnectionResetError as e: + xmppserverlog.debug("{}".format(e)) + self._set_state("DISCONNECT") + + except ConnectionAbortedError as e: + xmppserverlog.debug("{}".format(e)) + self._set_state("DISCONNECT") + + except OSError as e: + xmppserverlog.debug("{}".format(e)) + + except Exception as e: + xmppserverlog.exception("{}".format(e)) + + def _disconnect(self): + try: + + bot = bumper.bot_get(self.uid) + if bot: + bumper.bot_set_xmpp(bot["did"], False) + + client = bumper.client_get(self.clientresource) + if client: + bumper.client_set_xmpp(client["resource"], False) + + self.connection.close() + + except Exception as e: + xmppserverlog.exception("{}".format(e)) + + def _tag_strip_uri(self, tag): + try: + if tag[0] == "{": + _, _, tag = tag[1:].partition("}") + return tag + + except Exception as 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 + ) + ) + + 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): + try: + + if "roster" in data: + # Return not-implemented for roster + self.send( + ''.format( + xml.get("id") + ) + ) + return + + if xml.get("type") == "set": + if ( + "com:sf" in data and xml.get("to") == "rl.ecorobot.net" + ): # Android bind? Not sure what this does yet. + self.send( + ''.format( + xml.get("id"), + self.uid, + XMPPServer.server_id, + self.clientresource, + ) + ) + + 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 + + # forward + for client in XMPPServer.clients: + if ( + client.bumper_jid != self.bumper_jid + and client.state == client.READY + ): + ctl_to = xml.get("to") + xml.attrib["from"] = "{}".format(self.bumper_jid) + rxmlstring = ET.tostring(xml).decode("utf-8") + # clean up string to remove namespaces added by ET + rxmlstring = rxmlstring.replace("xmlns:ns0=", "xmlns=") + rxmlstring = rxmlstring.replace("ns0:", "") + rxmlstring = rxmlstring.replace('iq xmlns="com:ctl"', "iq") + rxmlstring = rxmlstring.replace("'.format( + uuid.uuid4(), adminuser, self.bumper_jid, newuser + ) + xmppserverlog.debug("Add User: {}".format(adduser)) + self.send(adduser) + + # Add user ACs - Manage users, settings, and clean (full access) + adduseracs = ''.format( + uuid.uuid4(), adminuser, self.bumper_jid, newuser + ) + xmppserverlog.debug("Add User ACs: {}".format(adduseracs)) + self.send(adduseracs) + + # GetUserInfo - Just to confirm it set correctly + self.send( + ''.format( + uuid.uuid4(), adminuser, self.bumper_jid + ) + ) + + else: + rxmlstring = ET.tostring(xml).decode("utf-8") + # clean up string to remove namespaces added by ET + rxmlstring = rxmlstring.replace("xmlns:ns0=", "xmlns=") + rxmlstring = rxmlstring.replace("ns0:", "") + rxmlstring = rxmlstring.replace('iq xmlns="com:ctl"', "iq") + rxmlstring = rxmlstring.replace(" -1: + sc = data.decode("utf-8").find("to=") + ec = data.decode("utf-8").find(".ecorobot.net") + if ec > -1: + self.devclass = data.decode("utf-8")[sc + 4 : ec] + # ack jabbr:client + # 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('') + + else: + self.send("") + + else: + if "jabber:iq:auth" in xml.tag: # Handle iq-auth + self._handle_iq_auth(xml) + elif ( + "urn:ietf:params:xml:ns:xmpp-sasl" in xml.tag + ): # Handle SASL Auth + self._handle_sasl_auth(xml) + else: + xmppserverlog.error("Couldn't handle: {}".format(xml)) + + elif self.state == self.INIT: + if xml == None: + # Client getting session after authentication + if data.decode("utf-8").find("jabber:client") > -1: + # ack jabbr:client + self.send( + ''.format( + XMPPServer.server_id + ) + ) + time.sleep(0.25) + # session + self.send( + '' + ) + + else: # Handle init bind + 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) + else: + xmppserverlog.error("Couldn't handle: {}".format(xml)) + + except Exception as e: + xmppserverlog.exception("{}".format(e)) + + def _handle_iq_auth(self, data): + try: + 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, 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 = "" + 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 + + 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.bot_add("", self.uid, "", resource, "eco-legacy") + 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"))) + + else: + auth = False + if bumper.check_authcode(self.uid, authcode): + auth = True + elif bumper.use_auth == False: + auth = True + + if auth: + bumper.client_add(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 - {} - {}".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) + ) + else: + xmppserverlog.debug( + "xml parse error - {} - {}".format(data.decode("utf-8"), e) + ) + + except Exception as e: + xmppserverlog.exception("{}".format(e)) + + def _handle_sasl_auth(self, xml): + try: + + saslauth = base64.b64decode(xml.text).decode("utf-8").split("/") + username = saslauth[0] + username = saslauth[0].split("\x00")[1] + self.uid = username + if len(saslauth) > 1: + resource = saslauth[1] + self.clientresource = resource + elif len(saslauth[0].split("\x00")) > 2: + resource = saslauth[0].split("\x00")[2] + self.clientresource = resource + + if len(saslauth) > 2: + authcode = saslauth[2] + + if not self.uid.startswith("fuid"): + # Need sample data to see details here + bumper.bot_add(self.uid, self.uid, self.devclass, "atom", "eco-legacy") + self.type = self.BOT + xmppserverlog.info("bot authenticated {}".format(self.uid)) + # Send response + self.send( + '' + ) # Success + + # Client authenticated, move to next state + self._set_state("INIT") + + else: + auth = False + if bumper.check_authcode(self.uid, authcode): + auth = True + elif bumper.use_auth == False: + auth = True + + if auth: + self.type = self.CONTROLLER + bumper.client_add(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 Exception as e: + xmppserverlog.exception("{}".format(e)) + + def _handle_bind(self, xml): + try: + + bot = bumper.bot_get(self.uid) + if bot: + bumper.bot_set_xmpp(bot["did"], True) + + client = bumper.client_get(self.clientresource) + if client: + bumper.client_set_xmpp(client["resource"], True) + + clientbindxml = xml.getchildren() + clientresourcexml = clientbindxml[0].getchildren() + if self.devclass: # its a bot + self.name = "XMPP_Client_{}_{}".format(self.uid, self.devclass) + self.bumper_jid = "{}@{}.ecorobot.net/atom".format( + self.uid, self.devclass + ) + xmppserverlog.debug("new bot {}".format(self.uid)) + res = '{}'.format( + xml.get("id"), self.bumper_jid + ) + elif len(clientresourcexml) > 0: + self.clientresource = clientresourcexml[0].text + self.name = "XMPP_Client_{}".format(self.clientresource) + self.bumper_jid = "{}@{}/{}".format( + self.uid, XMPPServer.server_id, self.clientresource + ) + xmppserverlog.debug( + "new client {} using resource {}".format( + self.uid, self.clientresource + ) + ) + res = '{}'.format( + xml.get("id"), self.bumper_jid + ) + else: + self.name = "XMPP_Client_{}_{}".format(self.uid, self.address) + self.bumper_jid = "{}@{}".format(self.uid, XMPPServer.server_id) + xmppserverlog.debug("new client {}".format(self.uid)) + res = '{}'.format( + xml.get("id"), self.bumper_jid + ) + + self._set_state("BIND") + self.send(res) + + except Exception as 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": + xmppserverlog.debug( + "bot presence {} ".format(ET.tostring(xml, encoding="utf-8")) + ) + # Most likely a bot, possibly hello world in text + + # Send dummy return + self.send( + ' dummy '.format(self.bumper_jid) + ) + + # If it is a BOT, send extras + if self.type == self.BOT: + # get device info + self.send( + ''.format( + self.bumper_jid, XMPPServer.server_id + ) + ) + + else: + xmppserverlog.debug( + "client presence - {} ".format(ET.tostring(xml, encoding="utf-8")) + ) + + if xml.get("type") == "available": + xmppserverlog.debug( + "client presence available - {} ".format( + ET.tostring(xml, encoding="utf-8") + ) + ) + # Send dummy return + self.send( + ' dummy '.format(self.bumper_jid) + ) + elif xml.get("type") == "unavailable": + xmppserverlog.debug( + "client presence unavailable (DISCONNECT) - {} ".format( + ET.tostring(xml, encoding="utf-8") + ) + ) + + self._set_state("DISCONNECT") + else: + # Sometimes the android app sends these + xmppserverlog.debug( + "client presence (UNKNOWN) - {} ".format( + ET.tostring(xml, encoding="utf-8") + ) + ) + # Send dummy return + self.send( + ' dummy '.format(self.bumper_jid) + ) + + except Exception as e: + xmppserverlog.exception("{}".format(e)) + + def _parse_data(self, data): + + if data.decode("utf-8").startswith( + "]+\?>)", r"", data.decode("utf-8")) + "" + ) + + else: + newdata = "{}".format( + data.decode("utf-8") + ) # Add artificial root + + try: + root = ET.fromstring(newdata) + for item in root.iter(): + if item.tag != "root": + if item.tag == "iq": + if self.log_incoming_data: + xmppserverlog.debug( + "from {} - {}".format( + self.address, + str( + ET.tostring(item, encoding="utf-8").decode( + "utf-8" + ) + ).replace("ns0:", ""), + ) + ) + self._handle_iq(item, newdata) + item.clear() + + elif "auth" in item.tag: + if "urn:ietf:params:xml:ns:xmpp-sasl" in item.tag: # SASL Auth + self._handle_sasl_auth(item) + item.clear() + + elif "presence" in item.tag: + self._handle_presence(item) + item.clear() + + else: + if self.log_incoming_data: + xmppserverlog.debug( + "Unparsed Item - {}".format( + str( + ET.tostring(item, encoding="utf-8").decode( + "utf-8" + ) + ).replace("ns0:", "") + ) + ) + + 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 newdata: + xmppserverlog.error("xml parse error - {} - {}".format(newdata, e)) + else: + self.send("") # Close stream + + else: + if "" in newdata: + xmppserverlog.error( + "xml parse error - {} - {}".format(newdata, e) + ) + else: + self.send("") # Close stream + self._set_state("DISCONNECT") + + except Exception as e: + xmppserverlog.exception("{}".format(e)) + + def _handle_iq(self, xml, data): + try: + 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) + elif child == "session": + self._handle_session(xml) + elif child == "ping": + self._handle_ping(xml, data) + elif child == "query": + if self.type == self.BOT: + self._handle_result(xml, data) + else: + self._handle_ctl(xml, data) + elif xml.get("type") == "result": + if self.type == self.BOT: + self._handle_result(xml, data) + else: + self._handle_result(xml, data) + elif xml.get("type") == "set": + if self.type == self.BOT: + self._handle_result(xml, data) + else: + self._handle_result(xml, data) + + except Exception as e: + xmppserverlog.exception("{}".format(e)) + + def run(self): + # xmppserverlog.info('client connected - {}'.format(self.address)) + await self._set_state("CONNECT") + while not self.state == self.DISCONNECT and not self.connection._closed: + data = b"" + time.sleep(0.1) + if not self.connection._closed: + try: + data = self.connection.recv(4096) + if data != b"": + self._parse_data(data) + except ConnectionResetError as e: + xmppserverlog.debug("{}".format(e)) + except OSError as e: + xmppserverlog.debug("{}".format(e)) + except Exception as e: + xmppserverlog.exception("{}".format(e)) diff --git a/bumper/xmppserver.py b/bumper/xmppserver.py index e044790..5ad8e22 100644 --- a/bumper/xmppserver.py +++ b/bumper/xmppserver.py @@ -4,8 +4,8 @@ from threading import Thread import sys, socket, threading, re, time, logging, uuid, xml.etree.ElementTree as ET import base64 import ssl -import contextvars import bumper +import asyncio xmppserverlog = logging.getLogger("xmppserver") @@ -19,88 +19,116 @@ class XMPPServer: def __init__(self, address): # Initialize bot server self.address = address + self.aclients = {} # task -> (reader, writer) - 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() - - else: - try: - self.run_server() - except KeyboardInterrupt: - self.disconnect() - - 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 - 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 + async def async_server(self): + xmppserverlog.debug( + "listening on {}:{}".format(self.address[0], self.address[1]) + ) + server = await asyncio.start_server( + self.accept_client, self.address[0], self.address[1] ) - self.socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM) - self.socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) + await server.serve_forever() - try: - self.socket.bind(self.address) - self.socket.listen(5) + # self.clients = {} # task -> (reader, writer) - xmppserverlog.debug( - "listening on {}:{}".format(self.address[0], self.address[1]) - ) - while not self.exit_flag: - connection, client_address = self.socket.accept() + def accept_client(self, client_reader, client_writer): + # task = asyncio.Task(self.handle_client(client_reader, client_writer)) + aclient = XMPPAsyncClient(client_reader, client_writer) + task = asyncio.Task(aclient.handle_async_client()) + self.aclients[task] = (client_reader, client_writer) - # 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 - ) - ) - client._disconnect() - self.remove_client_byip(client.address) + def client_done(task): + del self.aclients[task] + xmppserverlog.info("End Connection for {}".format(client_writer.get_extra_info("peername"))) + client_writer.close() - xmppserverlog.debug( - "starting new client with ip {}".format(client_address[0]) - ) - thread_id = uuid.uuid4() - client = Client(thread_id, connection, client_address) - client.setDaemon(True) - client.start() - self.clients.append(client) + clientaddr = client_writer.get_extra_info("peername") + xmppserverlog.info("New Connection from {}".format(clientaddr)) + task.add_done_callback(client_done) - 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) + # 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() - except Exception as e: - xmppserverlog.exception("{}".format(e)) - exit(1) + # else: + # try: + # self.run_server() + # except KeyboardInterrupt: + # self.disconnect() - except KeyboardInterrupt as e: - xmppserverlog.exception("{}".format(e)) + # def run_server(self): + # logging.info("Starting XMPP Server at {}".format(self.address)) + # print("Starting XMPP Server at {}".format(self.address)) - finally: - connection.shutdown(socket.SHUT_RDWR) - connection.close() - self.disconnect() - xmppserverlog.info("disconnecting") + # # xmppserverlog.setLevel(logging.DEBUG) - self.socket.close() + # # 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) + + # try: + # self.socket.bind(self.address) + # self.socket.listen(5) + + # 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 + # ) + # ) + # client._disconnect() + # self.remove_client_byip(client.address) + + # xmppserverlog.debug( + # "starting new client with ip {}".format(client_address[0]) + # ) + # thread_id = uuid.uuid4() + # client = XMPPAsyncClient(thread_id, connection, client_address) + # 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) + + # except Exception as e: + # xmppserverlog.exception("{}".format(e)) + # exit(1) + + # except KeyboardInterrupt as e: + # xmppserverlog.exception("{}".format(e)) + + # finally: + # connection.shutdown(socket.SHUT_RDWR) + # connection.close() + # self.disconnect() + # xmppserverlog.info("disconnecting") + + # self.socket.close() def disconnect(self): try: @@ -148,7 +176,7 @@ class XMPPServer: self.clients.remove(client) -class Client(threading.Thread): +class XMPPAsyncClient: IDLE = 0 CONNECT = 1 INIT = 2 @@ -159,43 +187,63 @@ class Client(threading.Thread): BOT = 1 CONTROLLER = 2 - def __init__(self, thread_id, connection, client_address): - threading.Thread.__init__(self) - self.id = thread_id - self.name = "XMPP_Client_{}".format(client_address[0]) + def __init__(self, client_reader, client_writer): self.type = self.UNKNOWN self.state = self.IDLE - self.connection = connection - self.address = client_address[0] + self.address = client_writer.get_extra_info("peername") + self.client_reader = client_reader + self.client_writer = client_writer self.clientresource = "" self.devclass = "" self.bumper_jid = "" self.uid = "" - self.log_sent_message = False # Set to true to log sends + self.log_sent_message = True # Set to true to log sends self.log_incoming_data = True # Set to true to log sends - xmppserverlog.debug( - "new client thread init for client with ip {}".format(self.address) - ) + xmppserverlog.debug("new client with ip {}".format(self.address)) - def send(self, command): + async def handle_async_client(self): + # xmppserverlog.info('client connected - {}'.format(self.address)) + #await self._set_state("READY") + await self._set_state("CONNECT") + #asyncio.Task(self.send_ping(30)) + while True: + await asyncio.sleep(0.1) + if not self.state == self.DISCONNECT: + data = await self.client_reader.read(4096) + # data = await asyncio.wait_for(client_reader.readline(), timeout=10.0) + if data is None: + xmppserverlog.warning("Received no data") + # exit loop and disconnect + return + + await self._parse_data(data) + else: + break + + # exit loop and disconnect + return + + async 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()) + # if not self.connection._closed: + if self.log_sent_message: + xmppserverlog.debug("send {} - {}".format(self.address, command)) + # self.connection.send(command.encode()) + self.client_writer.write(command.encode()) + await self.client_writer.drain() except BrokenPipeError as e: xmppserverlog.debug("{}".format(e)) - self._set_state("DISCONNECT") + await self._set_state("DISCONNECT") except ConnectionResetError as e: xmppserverlog.debug("{}".format(e)) - self._set_state("DISCONNECT") + await self._set_state("DISCONNECT") except ConnectionAbortedError as e: xmppserverlog.debug("{}".format(e)) - self._set_state("DISCONNECT") + await self._set_state("DISCONNECT") except OSError as e: xmppserverlog.debug("{}".format(e)) @@ -203,7 +251,7 @@ class Client(threading.Thread): except Exception as e: xmppserverlog.exception("{}".format(e)) - def _disconnect(self): + async def _disconnect(self): try: bot = bumper.bot_get(self.uid) @@ -214,12 +262,12 @@ class Client(threading.Thread): if client: bumper.client_set_xmpp(client["resource"], False) - self.connection.close() + self.client_writer.close() except Exception as e: xmppserverlog.exception("{}".format(e)) - def _tag_strip_uri(self, tag): + async def _tag_strip_uri(self, tag): try: if tag[0] == "{": _, _, tag = tag[1:].partition("}") @@ -228,9 +276,9 @@ class Client(threading.Thread): except Exception as e: xmppserverlog.exception("{}".format(e)) - def _set_state(self, state): + async def _set_state(self, state): try: - new_state = getattr(Client, state) + new_state = getattr(XMPPAsyncClient, state) if self.state > new_state: raise Exception( "{} illegal state change {}->{}".format( @@ -243,28 +291,46 @@ class Client(threading.Thread): self.state = new_state if new_state == 5: - self._disconnect() + await self._disconnect() except Exception as e: xmppserverlog.exception("{}".format(e)) - def _handle_ctl(self, xml, data): + async def _handle_ctl(self, xml, data): try: if "roster" in data: # Return not-implemented for roster - self.send( + await self.send( ''.format( xml.get("id") ) ) return + + if "disco#items" in data: + # Return not-implemented for disco#items + await self.send( + ''.format( + xml.get("id") + )) + return + + if "disco#info" in data: + # Return not-implemented for disco#info + await self.send( + ''.format( + xml.get("id") + ) + ) + return + if xml.get("type") == "set": if ( "com:sf" in data and xml.get("to") == "rl.ecorobot.net" ): # Android bind? Not sure what this does yet. - self.send( + await self.send( ''.format( xml.get("id"), self.uid, @@ -273,7 +339,7 @@ class Client(threading.Thread): ) ) - if xml[0][0]: + if len(xml[0]) > 0: ctl = xml[0][0] if ctl.get("admin") and self.type == self.BOT: xmppserverlog.debug( @@ -307,7 +373,7 @@ class Client(threading.Thread): except Exception as e: xmppserverlog.exception("{}".format(e)) - def _handle_ping(self, xml, data): + async def _handle_ping(self, xml, data): try: if xml.get("to").find("@") == -1: # No to address # Ping to server - respond @@ -315,7 +381,7 @@ class Client(threading.Thread): xml.get("id"), xml.get("to") ) # xmppserverlog.debug("Server Ping resp: {}".format(pingresp)) - self.send(pingresp) + await self.send(pingresp) else: pingto = xml.get("to") @@ -346,7 +412,14 @@ class Client(threading.Thread): except Exception as e: xmppserverlog.exception("{}".format(e)) - def _handle_result(self, xml, data): + async def send_ping(self, time): + if not self.state == 5: #disconnected + pingstring = "".format(XMPPServer.server_id, self.bumper_jid) + await self.send(pingstring) + await asyncio.sleep(time) + asyncio.Task(self.send_ping(time)) + + async def _handle_result(self, xml, data): try: ctl_to = xml.get("to") xml.attrib["from"] = self.bumper_jid @@ -370,17 +443,17 @@ class Client(threading.Thread): uuid.uuid4(), adminuser, self.bumper_jid, newuser ) xmppserverlog.debug("Add User: {}".format(adduser)) - self.send(adduser) + await self.send(adduser) # Add user ACs - Manage users, settings, and clean (full access) adduseracs = ''.format( uuid.uuid4(), adminuser, self.bumper_jid, newuser ) xmppserverlog.debug("Add User ACs: {}".format(adduseracs)) - self.send(adduseracs) + await self.send(adduseracs) # GetUserInfo - Just to confirm it set correctly - self.send( + await self.send( ''.format( uuid.uuid4(), adminuser, self.bumper_jid ) @@ -430,7 +503,7 @@ class Client(threading.Thread): except Exception as e: xmppserverlog.exception("{}".format(e)) - def _handle_connect(self, data, xml=None): + async def _handle_connect(self, data, xml=None): try: if self.state == self.CONNECT: @@ -443,30 +516,32 @@ class Client(threading.Thread): self.devclass = data.decode("utf-8")[sc + 4 : ec] # ack jabbr:client # no STARTTLS - self.send( + await self.send( ''.format( XMPPServer.server_id ) ) # with STARTTLS - # self.send(''.format(XMPPServer.server_id)) - time.sleep(0.25) + # await self.send(''.format(XMPPServer.server_id)) + + await asyncio.sleep(0.25) + #time.sleep(0.25) # send authentication support for iq-auth (fallback) and SASL - self.send( + await self.send( 'PLAIN' ) - # self.send('') + # await self.send('') else: - self.send("") + await self.send("") else: if "jabber:iq:auth" in xml.tag: # Handle iq-auth - self._handle_iq_auth(xml) + await self._handle_iq_auth(xml) elif ( "urn:ietf:params:xml:ns:xmpp-sasl" in xml.tag ): # Handle SASL Auth - self._handle_sasl_auth(xml) + await self._handle_sasl_auth(xml) else: xmppserverlog.error("Couldn't handle: {}".format(xml)) @@ -475,33 +550,34 @@ class Client(threading.Thread): # Client getting session after authentication if data.decode("utf-8").find("jabber:client") > -1: # ack jabbr:client - self.send( + await self.send( ''.format( XMPPServer.server_id ) ) - time.sleep(0.25) + await asyncio.sleep(0.25) + #time.sleep(0.25) # session - self.send( + await self.send( '' ) else: # Handle init bind if len(xml): - child = self._tag_strip_uri(xml[0].tag) + child = await self._tag_strip_uri(xml[0].tag) else: child = None if xml.tag == "iq": if child == "bind": - self._handle_bind(xml) + await self._handle_bind(xml) else: xmppserverlog.error("Couldn't handle: {}".format(xml)) except Exception as e: xmppserverlog.exception("{}".format(e)) - def _handle_iq_auth(self, data): + async def _handle_iq_auth(self, data): try: xml = ET.fromstring(data.decode("utf-8")) ctl = xml[0][0] @@ -512,7 +588,7 @@ class Client(threading.Thread): and "auth}username" in ctl.tag and self.type == self.UNKNOWN ): - self.send( + await self.send( ''.format( xml.get("id") ) @@ -527,6 +603,7 @@ class Client(threading.Thread): xmlauth = xml[0].getchildren() # uid = "" password = "" + authcode = "" resource = "" for aitem in xmlauth: if "username" in aitem.tag: @@ -547,10 +624,10 @@ class Client(threading.Thread): xmppserverlog.info("bot authenticated {}".format(self.uid)) # Client authenticated, move to next state - self._set_state("INIT") + await self._set_state("INIT") # Successful auth - self.send(''.format(xml.get("id"))) + await self.send(''.format(xml.get("id"))) else: auth = False @@ -564,14 +641,16 @@ class Client(threading.Thread): xmppserverlog.debug("client authenticated {}".format(self.uid)) # Client authenticated, move to next state - self._set_state("INIT") + await self._set_state("INIT") # Successful auth - self.send(''.format(xml.get("id"))) + await self.send( + ''.format(xml.get("id")) + ) else: # Failed auth - self.send( + await self.send( ''.format( xml.get("id") ) @@ -594,12 +673,13 @@ class Client(threading.Thread): except Exception as e: xmppserverlog.exception("{}".format(e)) - def _handle_sasl_auth(self, xml): + async def _handle_sasl_auth(self, xml): try: saslauth = base64.b64decode(xml.text).decode("utf-8").split("/") username = saslauth[0] username = saslauth[0].split("\x00")[1] + authcode = "" self.uid = username if len(saslauth) > 1: resource = saslauth[1] @@ -617,12 +697,12 @@ class Client(threading.Thread): self.type = self.BOT xmppserverlog.info("bot authenticated {}".format(self.uid)) # Send response - self.send( + await self.send( '' ) # Success # Client authenticated, move to next state - self._set_state("INIT") + await self._set_state("INIT") else: auth = False @@ -637,23 +717,23 @@ class Client(threading.Thread): xmppserverlog.debug("client authenticated {}".format(self.uid)) # Client authenticated, move to next state - self._set_state("INIT") + await self._set_state("INIT") # Send response - self.send( + await self.send( '' ) # Success else: # Failed to authenticate - self.send( + await self.send( '' ) # Fail except Exception as e: xmppserverlog.exception("{}".format(e)) - def _handle_bind(self, xml): + async def _handle_bind(self, xml): try: bot = bumper.bot_get(self.uid) @@ -697,22 +777,24 @@ class Client(threading.Thread): xml.get("id"), self.bumper_jid ) - self._set_state("BIND") - self.send(res) + await self._set_state("BIND") + await self.send(res) except Exception as e: xmppserverlog.exception("{}".format(e)) - def _handle_session(self, xml): + async def _handle_session(self, xml): try: res = ''.format(xml.get("id")) - self._set_state("READY") - self.send(res) + await self._set_state("READY") + await self.send(res) + asyncio.Task(self.send_ping(30)) + except Exception as e: xmppserverlog.exception("{}".format(e)) - def _handle_presence(self, xml): + async def _handle_presence(self, xml): try: if len(xml) and xml[0].tag == "status": @@ -722,24 +804,28 @@ class Client(threading.Thread): # Most likely a bot, possibly hello world in text # Send dummy return - self.send( + await self.send( ' dummy '.format(self.bumper_jid) ) + + # If it is a BOT, send extras if self.type == self.BOT: # get device info - self.send( + await self.send( ''.format( self.bumper_jid, XMPPServer.server_id ) ) + + else: xmppserverlog.debug( "client presence - {} ".format(ET.tostring(xml, encoding="utf-8")) ) - + if xml.get("type") == "available": xmppserverlog.debug( "client presence available - {} ".format( @@ -747,7 +833,7 @@ class Client(threading.Thread): ) ) # Send dummy return - self.send( + await self.send( ' dummy '.format(self.bumper_jid) ) elif xml.get("type") == "unavailable": @@ -757,7 +843,7 @@ class Client(threading.Thread): ) ) - self._set_state("DISCONNECT") + await self._set_state("DISCONNECT") else: # Sometimes the android app sends these xmppserverlog.debug( @@ -766,14 +852,14 @@ class Client(threading.Thread): ) ) # Send dummy return - self.send( + await self.send( ' dummy '.format(self.bumper_jid) ) except Exception as e: xmppserverlog.exception("{}".format(e)) - def _parse_data(self, data): + async def _parse_data(self, data): if data.decode("utf-8").startswith( "" in newdata: xmppserverlog.error("xml parse error - {} - {}".format(newdata, e)) else: - self.send("") # Close stream + await self.send("") # Close stream else: if "" in newdata: xmppserverlog.error( "xml parse error - {} - {}".format(newdata, e) ) else: - self.send("") # Close stream - self._set_state("DISCONNECT") + await self.send("") # Close stream + await self._set_state("DISCONNECT") except Exception as e: xmppserverlog.exception("{}".format(e)) - def _handle_iq(self, xml, data): + async def _handle_iq(self, xml, data): try: if len(xml): - child = self._tag_strip_uri(xml[0].tag) + child = await self._tag_strip_uri(xml[0].tag) else: child = None if xml.tag == "iq": if child == "bind": - self._handle_bind(xml) + await self._handle_bind(xml) elif child == "session": - self._handle_session(xml) + await self._handle_session(xml) elif child == "ping": - self._handle_ping(xml, data) + await self._handle_ping(xml, data) elif child == "query": if self.type == self.BOT: - self._handle_result(xml, data) + await self._handle_result(xml, data) else: - self._handle_ctl(xml, data) + await self._handle_ctl(xml, data) elif xml.get("type") == "result": if self.type == self.BOT: - self._handle_result(xml, data) + await self._handle_result(xml, data) else: - self._handle_result(xml, data) + await self._handle_result(xml, data) elif xml.get("type") == "set": if self.type == self.BOT: - self._handle_result(xml, data) + await self._handle_result(xml, data) else: - self._handle_result(xml, data) + await self._handle_result(xml, data) except Exception as e: xmppserverlog.exception("{}".format(e)) - - 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"" - time.sleep(0.1) - if not self.connection._closed: - try: - data = self.connection.recv(4096) - if data != b"": - self._parse_data(data) - except ConnectionResetError as e: - xmppserverlog.debug("{}".format(e)) - except OSError as e: - xmppserverlog.debug("{}".format(e)) - except Exception as e: - xmppserverlog.exception("{}".format(e)) - diff --git a/start_bumper.py b/start_bumper.py index 1975e80..2503f44 100644 --- a/start_bumper.py +++ b/start_bumper.py @@ -5,10 +5,14 @@ import bumper import sys, socket import time import platform +import os +os.environ['PYTHONASYNCIODEBUG'] = '1' +import asyncio def main(): args = sys.argv + listen_host = "" if len(args) > 0: if "--debug" in args: @@ -23,16 +27,14 @@ def main(): ) # format="[%(asctime)s] :: %(levelname)s :: %(name)s :: %(module)s :: %(funcName)s :: %(lineno)d :: %(message)s") - listen_host = args.index("--listen") - if (len(args) - 1) >= (listen_host + 1): - listen_host = args[listen_host+1] - else: + if "--listen" in args: + listen_host = args[args.index("--listen") + 1] + + if listen_host == "": 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 = socket.gethostbyname(socket.gethostname()) conf_address_443 = (listen_host, 443) conf_address_8007 = (listen_host, 8007) @@ -51,30 +53,46 @@ def main(): conf_address_8007, usessl=False, 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) - # bumper.bumper_users_var.set(users) + try: + loop = asyncio.get_event_loop() + except: + loop = asyncio.new_event_loop() + + # Start web servers + loop.set_debug(True) + conf_server.confserver_app() + conf_server_2.confserver_app() + asyncio.ensure_future(conf_server.start_server(),loop=loop) + asyncio.ensure_future(conf_server_2.start_server(),loop=loop) + + # Start MQTT Server + asyncio.ensure_future(mqtt_server.broker_coro()) + + # Start MQTT Helperbot + asyncio.ensure_future(mqtt_helperbot.start_helper_bot()) + + # Start XMPP Server + asyncio.ensure_future(xmpp_server.async_server()) + + loop.run_forever() + # 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 + #mqtt_server.run(run_async=True) # Start in new thread - time.sleep(1.5) # Wait for broker startup + #time.sleep(1.5) # Wait for broker startup # start mqtt_helperbot (async) - mqtt_helperbot.run(run_async=True) # Start in new thread + #mqtt_helperbot.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 + #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 + #conf_server_2.run(run_async=True) # Start in new thread while True: try: