additional optimizations and permission handling

Optimizations for handling XMPP data and commands

Basic permission handling - Add fuid users to bot
- Ozmo has ACLs and an "owner", if bot comes from eco to bumper the fuid_user won't have permissions
- Bumper will add the fuid_ user to bot ACLs and grant full
  - This can be used later when bumper adds proper auth to allow/block users
This commit is contained in:
Brian Martin 2019-02-27 09:05:23 -05:00
parent af29f69a99
commit a7ce14eb9c
2 changed files with 249 additions and 214 deletions

View file

@ -652,22 +652,32 @@ class ConfServer:
json_body = json.loads(await request.text()) json_body = json.loads(await request.text())
randomid = "".join(random.sample(string.ascii_letters, 6)) randomid = "".join(random.sample(string.ascii_letters, 6))
bots = self.bumper_bots.get() bots = self.bumper_bots.get()
for bot in bots: if "toId" in json_body: #Its a command
if bot.did == json_body["toId"] and bot.mqtt_connection == True: for bot in bots:
retcmd = await self.helperbot.send_command(json_body, randomid) if bot.company == 'eco-ng':
body = retcmd if bot.did == json_body["toId"] and bot.mqtt_connection == True:
confserverlog.debug( retcmd = await self.helperbot.send_command(json_body, randomid)
"\r\n POST: {} \r\n Response: {}".format(json_body, body) body = retcmd
confserverlog.debug(
"\r\n POST: {} \r\n Response: {}".format(json_body, body)
)
return web.json_response(body)
#No response, send error back
confserverlog.error(
"No bots with DID: {} connected to MQTT".format(
json_body["toId"]
) )
return web.json_response(body) )
else: body = {"id": randomid, "errno": bumper.ERR_COMMON, "ret": "fail"}
confserverlog.error( return web.json_response(body)
"No bots with DID: {} connected to MQTT".format( else:
json_body["toId"] if "td" in json_body: #Seen when doing initial wifi config
) if json_body["td"] == "PollSCResult":
) body = {
body = {"id": randomid, "errno": bumper.ERR_COMMON, "ret": "fail"} "ret": "ok"
return web.json_response(body) }
return web.json_response(body)
except Exception as e: except Exception as e:
confserverlog.exception("{}".format(e)) confserverlog.exception("{}".format(e))

View file

@ -11,8 +11,7 @@ xmppserverlog = logging.getLogger("xmppserver")
class XMPPServer: class XMPPServer:
server_id = "bumper" server_id = "ecouser.net"
bot_id = "bumpy"
client_id = None client_id = None
clients = [] clients = []
exit_flag = False exit_flag = False
@ -218,7 +217,7 @@ class Client(threading.Thread):
except BrokenPipeError as e: except BrokenPipeError as e:
xmppserverlog.error("{}".format(e)) xmppserverlog.error("{}".format(e))
# self._set_state('DISCONNECT') self._set_state('DISCONNECT')
except ConnectionResetError as e: except ConnectionResetError as e:
xmppserverlog.error("{}".format(e)) xmppserverlog.error("{}".format(e))
@ -282,7 +281,7 @@ class Client(threading.Thread):
def _handle_ctl(self, xml, data): def _handle_ctl(self, xml, data):
try: try:
if data.decode("utf-8").find("roster") > -1: if "roster" in data:
# Return not-implemented for roster # Return not-implemented for roster
self.send( self.send(
'<iq type="error" id="{}"><error type="cancel" code="501"><feature-not-implemented xmlns="urn:ietf:params:xml:ns:xmpp-stanzas"/></error></iq>'.format( '<iq type="error" id="{}"><error type="cancel" code="501"><feature-not-implemented xmlns="urn:ietf:params:xml:ns:xmpp-stanzas"/></error></iq>'.format(
@ -293,7 +292,7 @@ class Client(threading.Thread):
if xml.get("type") == "set": if xml.get("type") == "set":
if ( if (
data.decode("utf-8").find("com:sf") > -1 "com:sf" in data
and xml.get("to") == "rl.ecorobot.net" and xml.get("to") == "rl.ecorobot.net"
): # Android bind? Not sure what this does yet. ): # Android bind? Not sure what this does yet.
self.send( self.send(
@ -305,11 +304,6 @@ class Client(threading.Thread):
) )
) )
#else:
# xmppserverlog.debug(
# "Unknown set type: {}".format(data.decode("utf-8"))
# )
if xml[0][0]: if xml[0][0]:
ctl = xml[0][0] ctl = xml[0][0]
if ctl.get("admin") and self.type == self.BOT: if ctl.get("admin") and self.type == self.BOT:
@ -322,11 +316,8 @@ class Client(threading.Thread):
# forward # forward
for client in XMPPServer.clients: for client in XMPPServer.clients:
if client.bumper_jid != self.bumper_jid and client.state == client.READY: if client.bumper_jid != self.bumper_jid and client.state == client.READY:
#if client.address != self.address and client.state == client.READY:
ctl_to = xml.get("to") ctl_to = xml.get("to")
xml.attrib["from"] = "{}".format(self.bumper_jid)
#xml.attrib["from"] = self.bumper_jid#.replace("@{}".format(XMPPServer.server_id),"@ecouser.net")
xml.attrib["from"] = "{}@ecouser.net".format(self.uid)
rxmlstring = ET.tostring(xml).decode("utf-8") rxmlstring = ET.tostring(xml).decode("utf-8")
#clean up string to remove namespaces added by ET #clean up string to remove namespaces added by ET
rxmlstring = rxmlstring.replace("xmlns:ns0=", "xmlns=") rxmlstring = rxmlstring.replace("xmlns:ns0=", "xmlns=")
@ -338,20 +329,7 @@ class Client(threading.Thread):
if client.uid.lower() in ctl_to.lower(): if client.uid.lower() in ctl_to.lower():
xmppserverlog.info("Sending ctl to bot: {}".format(rxmlstring)) xmppserverlog.info("Sending ctl to bot: {}".format(rxmlstring))
client.send(rxmlstring) client.send(rxmlstring)
#client.send(data.decode("utf-8"))
# data = data.decode("utf-8")
# id_index = data.find("id")
# if id_index > -1:
# data = (
# data[:id_index]
# + 'from="'
# + XMPPServer.client_id
# + '" '
# + data[id_index:]
# )
# data = data.encode()
# client.send(data.decode("utf-8"))
except Exception as e: except Exception as e:
xmppserverlog.exception("{}".format(e)) xmppserverlog.exception("{}".format(e))
@ -391,97 +369,124 @@ class Client(threading.Thread):
xmppserverlog.exception("{}".format(e)) xmppserverlog.exception("{}".format(e))
def _handle_result(self, xml, data): def _handle_result(self, xml, data):
# forward
try: try:
ctl_to = xml.get("to") ctl_to = xml.get("to")
xml.attrib["from"] = self.bumper_jid xml.attrib["from"] = self.bumper_jid
rxmlstring = ET.tostring(xml).decode("utf-8") if "errno='103' error='permission denied," in data: #No permissions, usually if bot was last on Ecovac network
#clean up string to remove namespaces added by ET if self.type == self.BOT:
rxmlstring = rxmlstring.replace("xmlns:ns0=", "xmlns=") xquery = xml.getchildren()
rxmlstring = rxmlstring.replace("ns0:", "") ctl = xquery[0].getchildren()
rxmlstring = rxmlstring.replace('iq xmlns="com:ctl"', "iq") ctlerr = ctl[0].attrib["error"]
rxmlstring = rxmlstring.replace('<query','<query xmlns="com:ctl"') adminuser = ctlerr.replace("permission denied, please contact ","")
adminuser = adminuser.replace(" ","")
if not (adminuser.startswith("fuid_") or bumper.use_auth): #if not fuid_ then its ecovacs OR ignore bumper auth
#TODO: Implement auth later, should this user have access to bot?
#Add user jid to bot
newuser = ctl_to.split("/")[0]
adduser = '<iq type="set" id="{}" from="{}" to="{}"><query xmlns="com:ctl"><ctl td="AddUser" id="0000" jid="{}" /></query></iq>'.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 = '<iq type="set" id="{}" from="{}" to="{}"><query xmlns="com:ctl"><ctl td="SetAC" id="1111" jid="{}"><acs><ac name="userman" allow="1"/><ac name="setting" allow="1"/><ac name="clean" allow="1"/></acs></ctl></query></iq>'.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(
'<iq type="set" id="{}" from="{}" to="{}"><query xmlns="com:ctl"><ctl td="GetUserInfo" id="4444" /><UserInfos/></query></iq>'.format(
uuid.uuid4(), adminuser, self.bumper_jid)
)
if ctl_to == "de.ecorobot.net": #Send to all clients
xmppserverlog.info("Sending to all clients because of de: {}".format(rxmlstring))
for client in XMPPServer.clients:
client.send(rxmlstring)
else: 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('<query','<query xmlns="com:ctl"')
if self.type == self.BOT:
if ctl_to == "de.ecorobot.net": #Send to all clients
xmppserverlog.debug("Sending to all clients because of de: {}".format(rxmlstring))
for client in XMPPServer.clients:
client.send(rxmlstring)
if xml.get("to").find("@") == -1: #No to address if xml.get("to").find("@") == -1: #No to address
ctl_to = xml.get("to") ctl_to = xml.get("to")
else: else:
ctl_to = "{}@ecouser.net".format(ctl_to.split("@")[0]) ctl_to = "{}@ecouser.net".format(ctl_to.split("@")[0])
for client in XMPPServer.clients: for client in XMPPServer.clients:
if client.bumper_jid != self.bumper_jid and client.state == client.READY: if client.bumper_jid != self.bumper_jid and client.state == client.READY:
if ctl_to: if not "@" in ctl_to: #No user@, send to all clients?
if client.uid.lower() in ctl_to.lower(): #TODO: Revisit later, this may be wrong
#if ctl_to == client.bumper_jid:
xmppserverlog.info("Sending from {} to client {}: {}".format(self.uid, client.uid, rxmlstring))
client.send(rxmlstring)
#xmppserverlog.info("Sending to client: {}".format(data.decode("utf-8")))
#client.send(data.decode("utf-8"))
else: #no send to
#xmppserverlog.info("Sending to all clients: {}".format(rxmlstring))
client.send(rxmlstring) client.send(rxmlstring)
#if client.address != self.address and client.state == client.READY: elif client.uid.lower() in ctl_to.lower(): #If client matches TO=
#client.send(data.decode("utf-8")) xmppserverlog.debug("Sending from {} to client {}: {}".format(self.uid, client.uid, rxmlstring))
client.send(rxmlstring)
except Exception as e: except Exception as e:
xmppserverlog.exception("{}".format(e)) xmppserverlog.exception("{}".format(e))
def _handle_connect(self, data): def _handle_connect(self, data, xml=None):
try: try:
if self.state == self.CONNECT: if self.state == self.CONNECT:
# Client first connecting, send our features if xml == None:
# Client first connecting, send our features
if data.decode("utf-8").find("jabber:client") > -1: if data.decode("utf-8").find("jabber:client") > -1:
sc = data.decode("utf-8").find("to=") sc = data.decode("utf-8").find("to=")
ec = data.decode("utf-8").find(".ecorobot.net") ec = data.decode("utf-8").find(".ecorobot.net")
if ec > -1: if ec > -1:
self.devclass = data.decode("utf-8")[sc+4:ec] self.devclass = data.decode("utf-8")[sc+4:ec]
# ack jabbr:client # ack jabbr:client
# no STARTTLS # no STARTTLS
self.send( self.send(
'<stream:stream xmlns:stream="http://etherx.jabber.org/streams" xmlns="jabber:client" version="1.0" id="1" from="{}">'.format( '<stream:stream xmlns:stream="http://etherx.jabber.org/streams" xmlns="jabber:client" version="1.0" id="1" from="{}">'.format(
XMPPServer.server_id XMPPServer.server_id
)
) )
) # with STARTTLS
# with STARTTLS # self.send('<stream:stream xmlns:stream="http://etherx.jabber.org/streams" xmlns:tls="http://www.ietf.org/rfc/rfc2595.txt" xmlns="jabber:client" version="1.0" id="1" from="{}">'.format(XMPPServer.server_id))
# self.send('<stream:stream xmlns:stream="http://etherx.jabber.org/streams" xmlns:tls="http://www.ietf.org/rfc/rfc2595.txt" xmlns="jabber:client" version="1.0" id="1" from="{}">'.format(XMPPServer.server_id)) time.sleep(0.25)
time.sleep(0.25) # send authentication support for iq-auth (fallback) and SASL
# send authentication support for iq-auth (fallback) and SASL self.send(
self.send( '<stream:features><auth xmlns="http://jabber.org/features/iq-auth"/><mechanisms xmlns="urn:ietf:params:xml:ns:xmpp-sasl"><mechanism>PLAIN</mechanism></mechanisms></stream:features>'
'<stream:features><auth xmlns="http://jabber.org/features/iq-auth"/><mechanisms xmlns="urn:ietf:params:xml:ns:xmpp-sasl"><mechanism>PLAIN</mechanism></mechanisms></stream:features>' )
) # self.send('<stream:features><auth xmlns="http://jabber.org/features/iq-auth"/></stream:features>')
# self.send('<stream:features><auth xmlns="http://jabber.org/features/iq-auth"/></stream:features>')
elif data.decode("utf-8").find("jabber:iq:auth") > -1: # Handle iq-auth else:
self._handle_iq_auth(data) self.send("</stream>")
elif ( else:
data.decode("utf-8").find("urn:ietf:params:xml:ns:xmpp-sasl") > -1 if "jabber:iq:auth" in xml.tag: # Handle iq-auth
): # Handle SASL auth self._handle_iq_auth(xml)
self._handle_sasl_auth(data) 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: elif self.state == self.INIT:
# Client getting session after authentication if xml == None:
if data.decode("utf-8").find("jabber:client") > -1: # Client getting session after authentication
# ack jabbr:client if data.decode("utf-8").find("jabber:client") > -1:
self.send( # ack jabbr:client
'<stream:stream xmlns:stream="http://etherx.jabber.org/streams" xmlns="jabber:client" version="1.0" id="1" from="{}">'.format( self.send(
XMPPServer.server_id '<stream:stream xmlns:stream="http://etherx.jabber.org/streams" xmlns="jabber:client" version="1.0" id="1" from="{}">'.format(
XMPPServer.server_id
)
)
time.sleep(0.25)
# session
self.send(
'<stream:features><bind xmlns="urn:ietf:params:xml:ns:xmpp-bind"/><session xmlns="urn:ietf:params:xml:ns:xmpp-session"/></stream:features>'
) )
)
time.sleep(0.25)
# session
self.send(
'<stream:features><bind xmlns="urn:ietf:params:xml:ns:xmpp-bind"/><session xmlns="urn:ietf:params:xml:ns:xmpp-session"/></stream:features>'
)
else: # Handle init bind else: # Handle init bind
xml = ET.fromstring(data.decode("utf-8"))
if len(xml): if len(xml):
child = self._tag_strip_uri(xml[0].tag) child = self._tag_strip_uri(xml[0].tag)
else: else:
@ -490,6 +495,10 @@ class Client(threading.Thread):
if xml.tag == "iq": if xml.tag == "iq":
if child == "bind": if child == "bind":
self._handle_bind(xml) self._handle_bind(xml)
else:
xmppserverlog.error("Couldn't handle: {}".format(xml))
except Exception as e: except Exception as e:
xmppserverlog.exception("{}".format(e)) xmppserverlog.exception("{}".format(e))
@ -573,7 +582,7 @@ class Client(threading.Thread):
except ET.ParseError as e: except ET.ParseError as e:
if "no element found" in e.msg: if "no element found" in e.msg:
xmppserverlog.debug( xmppserverlog.debug(
"xml parse error - {} - {} - this is common with ecovac protocol".format( "xml parse error - {} - {}".format(
data.decode("utf-8"), e data.decode("utf-8"), e
) )
) )
@ -589,9 +598,9 @@ class Client(threading.Thread):
except Exception as e: except Exception as e:
xmppserverlog.exception("{}".format(e)) xmppserverlog.exception("{}".format(e))
def _handle_sasl_auth(self, data): def _handle_sasl_auth(self, xml):
try: try:
xml = ET.fromstring(data.decode("utf-8"))
saslauth = base64.b64decode(xml.text).decode("utf-8").split("/") saslauth = base64.b64decode(xml.text).decode("utf-8").split("/")
username = saslauth[0] username = saslauth[0]
username = saslauth[0].split("\x00")[1] username = saslauth[0].split("\x00")[1]
@ -609,6 +618,7 @@ class Client(threading.Thread):
if not self.uid.startswith("fuid"): if not self.uid.startswith("fuid"):
# Need sample data to see details here # Need sample data to see details here
bumper.add_bot(self.uid, self.uid, self.devclass, "atom","eco-legacy") bumper.add_bot(self.uid, self.uid, self.devclass, "atom","eco-legacy")
self.type = self.BOT
xmppserverlog.info("bot authenticated {}".format(self.uid)) xmppserverlog.info("bot authenticated {}".format(self.uid))
# Send response # Send response
self.send( self.send(
@ -626,6 +636,7 @@ class Client(threading.Thread):
auth = True auth = True
if auth: if auth:
self.type = self.CONTROLLER
bumper.add_client(self.uid, "bumper", self.clientresource) bumper.add_client(self.uid, "bumper", self.clientresource)
xmppserverlog.debug("client authenticated {}".format(self.uid)) xmppserverlog.debug("client authenticated {}".format(self.uid))
@ -643,22 +654,6 @@ class Client(threading.Thread):
'<response xmlns="urn:ietf:params:xml:ns:xmpp-sasl"/>' '<response xmlns="urn:ietf:params:xml:ns:xmpp-sasl"/>'
) # Fail ) # 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
)
)
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: except Exception as e:
xmppserverlog.exception("{}".format(e)) xmppserverlog.exception("{}".format(e))
@ -725,103 +720,148 @@ class Client(threading.Thread):
def _handle_presence(self, xml): def _handle_presence(self, xml):
try: try:
if len(xml) and xml[0].tag == "status": if len(xml) and xml[0].tag == "status":
# bot announcing arrival
self.type = self.BOT
xmppserverlog.debug( xmppserverlog.debug(
"{} type set to BOT (based on presence tag)".format(self.address) "bot presence {} ".format(ET.tostring(xml, encoding="utf-8"))
) )
# send a command from an unknown user - the response will contain the correct admin username #Most likely a bot, possibly hello world in text
#Send dummy return
self.send( self.send(
'<presence to="{}"> dummy </presence>'.format( '<presence to="{}"> dummy </presence>'.format(
self.bumper_jid self.bumper_jid
) )
) )
# self.send(
# '<iq type="set" id="14" to="{}@{}.{}/atom"><query xmlns="com:ctl"><ctl td="GetDeviceInfo"/></query></iq>'.format(
# self.uid, self.devclass, XMPPServer.server_id
# )
# )
self.send( #If it is a BOT, send extras
'<iq type="set" id="{}" from="{}" to="{}"><query xmlns="com:ctl"><ctl td="GetCleanState" /></query></iq>'.format( if self.type == self.BOT:
uuid.uuid4(), "unknown@ecouser.net", XMPPServer.server_id #get device info
self.send(
'<iq type="set" id="14" to="{}" from="{}"><query xmlns="com:ctl"><ctl td="GetDeviceInfo"/></query></iq>'.format(
self.bumper_jid, XMPPServer.server_id
)
) )
)
else: else:
self.type = self.CONTROLLER
xmppserverlog.debug( xmppserverlog.debug(
"{} type set to CONTROLLER (based on presence tag)".format( "client presence - {} ".format(ET.tostring(xml, encoding="utf-8"))
self.address
)
) )
self.send(
'<presence to="{}@{}/{}"> dummy </presence>'.format( if xml.get("type") == "available":
self.uid, XMPPServer.server_id, self.clientresource xmppserverlog.debug(
"client presence available - {} ".format(ET.tostring(xml, encoding="utf-8"))
) )
) #Send dummy return
self.send(
'<presence to="{}"> dummy </presence>'.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(
'<presence to="{}"> dummy </presence>'.format(
self.bumper_jid
)
)
except Exception as e: except Exception as e:
xmppserverlog.exception("{}".format(e)) xmppserverlog.exception("{}".format(e))
def _parse_data(self, data): def _parse_data(self, data):
if self.log_incoming_data:
xmppserverlog.debug(
"from {} - {}".format(self.address, data.decode("utf-8")) if data.decode("utf-8").startswith("<?xml"): #Strip <?xml and add artificial root
) newdata = re.sub(r"(<\?xml[^>]+\?>)", r"<root>",data.decode("utf-8")) + "</root>"
else:
newdata = "<root>{}</root>".format(data.decode("utf-8")) #Add artificial root
try: try:
xml = ET.fromstring(data.decode("utf-8")) root = ET.fromstring(newdata)
self._handle_xml(xml, data) 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:",""))
)
print("e")
except ET.ParseError as e: except ET.ParseError as e:
if ( if (
"no element found" in e.msg "no element found" in e.msg
): # Element not closed or not all bytes received ): # Element not closed or not all bytes received
# Happens wth connect stream often # Happens wth connect stream often
if "<stream:stream " in data.decode("utf-8"): if "<stream:stream " in newdata:
if self.state == self.CONNECT or self.state == self.INIT: if self.state == self.CONNECT or self.state == self.INIT:
self._handle_connect(data) self._handle_connect(newdata.encode("utf-8"))
else: else:
if not (data.decode("utf-8") == "" or data.decode("utf-8") == " "): if not (newdata == "" or newdata == " "):
xmppserverlog.error( xmppserverlog.error(
"xml parse error - {} - {}".format(data.decode("utf-8"), e) "xml parse error - {} - {}".format(newdata, e)
) )
elif "not well-formed (invalid token)" in e.msg: elif "not well-formed (invalid token)" in e.msg:
# If a lone </stream:stream> - client is signalling end of session/disconnect # If a lone </stream:stream> - client is signalling end of session/disconnect
if not "</stream:stream>" in data.decode("utf-8"): if not "</stream:stream>" in newdata:
xmppserverlog.error( xmppserverlog.error(
"xml parse error - {} - {}".format(data.decode("utf-8"), e) "xml parse error - {} - {}".format(newdata, e)
) )
else: else:
self.send("</stream:stream>") # Close stream self.send("</stream:stream>") # Close stream
elif (
"junk after document element" in e.msg
): # More than one xml doc in data
# try to split it
data0 = data.decode("utf-8")
data1 = data0[e.position[1] :]
data0 = data0[: e.position[1]]
# xmppserverlog.debug('xml parse error - {} - {} - split0: {} - split1: {}'.format(data.decode('utf-8'), e, data0, data1))
self._parse_data(data0.encode("utf-8"))
self._parse_data(data1.encode("utf-8"))
else: else:
xmppserverlog.debug( if "<stream:stream" in newdata: #Handle start stream and connect
"xml parse error - {} - {}".format(data.decode("utf-8"), e) if self.state == self.CONNECT or self.state == self.INIT:
) xmppserverlog.debug(
"Handling connect data - {}".format(newdata)
)
self._handle_connect(newdata.encode("utf-8"))
else:
if not "</stream:stream>" in newdata:
xmppserverlog.error(
"xml parse error - {} - {}".format(newdata, e)
)
else:
self.send("</stream:stream>") # Close stream
self._set_state("DISCONNECT")
except Exception as e: except Exception as e:
xmppserverlog.exception("{}".format(e)) xmppserverlog.exception("{}".format(e))
def _handle_xml(self, xml, data): def _handle_iq(self, xml, data):
try: try:
if self.state == self.CONNECT or self.state == self.INIT:
self._handle_connect(data)
if len(xml): if len(xml):
child = self._tag_strip_uri(xml[0].tag) child = self._tag_strip_uri(xml[0].tag)
else: else:
@ -850,25 +890,20 @@ class Client(threading.Thread):
else: else:
self._handle_result(xml, data) self._handle_result(xml, data)
if xml.tag == "presence":
self._handle_presence(xml)
except Exception as e: except Exception as e:
xmppserverlog.exception("{}".format(e)) xmppserverlog.exception("{}".format(e))
def run(self): def run(self):
# xmppserverlog.info('client connected - {}'.format(self.address)) # xmppserverlog.info('client connected - {}'.format(self.address))
self._set_state("CONNECT") self._set_state("CONNECT")
while not self.state == self.DISCONNECT and not self.connection._closed: while not self.state == self.DISCONNECT and not self.connection._closed:
data = b"" data = b""
time.sleep(0.1)
time.sleep(0.2)
if not self.connection._closed: if not self.connection._closed:
try: try:
#data = self.connection.recv(4096) data = self.connection.recv(4096)
data = self.connection.recv(8192) if data != b"":
self._parse_data(data)
except ConnectionResetError as e: except ConnectionResetError as e:
xmppserverlog.error("{}".format(e)) xmppserverlog.error("{}".format(e))
except OSError as e: except OSError as e:
@ -876,15 +911,5 @@ class Client(threading.Thread):
except Exception as e: except Exception as e:
xmppserverlog.exception("{}".format(e)) xmppserverlog.exception("{}".format(e))
if data != b"":
splitdata = data.decode("utf-8")
splitdata = splitdata.split("<iq")
if len(splitdata) > 1:
for s in splitdata:
if not s.startswith("<iq") and s:
self._parse_data("<iq {}".format(s).encode("utf-8"))
else:
self._parse_data(s.encode("utf-8"))
else:
self._parse_data(data)