Remove creepy xmpp thingy

This commit is contained in:
Basar Turgut 2020-06-27 01:33:12 +03:00
parent 8122919f77
commit 247e1a0977
15 changed files with 7 additions and 1983 deletions

View file

@ -2,7 +2,6 @@
from bumper.confserver import ConfServer from bumper.confserver import ConfServer
from bumper.mqttserver import MQTTServer, MQTTHelperBot from bumper.mqttserver import MQTTServer, MQTTHelperBot
from bumper.xmppserver import XMPPServer
from bumper.models import * from bumper.models import *
from bumper.db import * from bumper.db import *
import asyncio import asyncio
@ -43,9 +42,7 @@ server_cert = os.environ.get("BUMPER_CERT") or os.path.join(certs_dir, "bumper.c
server_key = os.environ.get("BUMPER_KEY") or os.path.join(certs_dir, "bumper.key") server_key = os.environ.get("BUMPER_KEY") or os.path.join(certs_dir, "bumper.key")
# Listeners # Listeners
bumper_listen = os.environ.get("BUMPER_LISTEN") or socket.gethostbyname( bumper_listen = os.environ.get("BUMPER_LISTEN") or "192.168.1.163"
socket.gethostname()
)
bumper_announce_ip = os.environ.get("BUMPER_ANNOUNCE_IP") or bumper_listen bumper_announce_ip = os.environ.get("BUMPER_ANNOUNCE_IP") or bumper_listen
@ -60,7 +57,6 @@ mqtt_server = None
mqtt_helperbot = None mqtt_helperbot = None
conf_server = None conf_server = None
conf_server_2 = None conf_server_2 = None
xmpp_server = None
# Plugins # Plugins
sys.path.append(os.path.join(bumper_dir, "bumper", "plugins")) sys.path.append(os.path.join(bumper_dir, "bumper", "plugins"))
@ -141,22 +137,12 @@ boterrorlog.addHandler(boterrorlog_rotate)
# Override the logging level # Override the logging level
# boterrorlog.setLevel(logging.INFO) # boterrorlog.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)
logging.getLogger("asyncio").setLevel(logging.CRITICAL + 1) # Ignore this logger logging.getLogger("asyncio").setLevel(logging.CRITICAL + 1) # Ignore this logger
mqtt_listen_port = 8883 mqtt_listen_port = 8883
conf1_listen_port = 443 conf1_listen_port = 443
conf2_listen_port = 8007 conf2_listen_port = 8007
xmpp_listen_port = 5223
async def start(): async def start():
@ -200,8 +186,6 @@ async def start():
conf_server = ConfServer((bumper_listen, conf1_listen_port), usessl=True) conf_server = ConfServer((bumper_listen, conf1_listen_port), usessl=True)
global conf_server_2 global conf_server_2
conf_server_2 = ConfServer((bumper_listen, conf2_listen_port), usessl=False) conf_server_2 = ConfServer((bumper_listen, conf2_listen_port), usessl=False)
global xmpp_server
xmpp_server = XMPPServer((bumper_listen, xmpp_listen_port))
# Start MQTT Server # Start MQTT Server
asyncio.create_task(mqtt_server.broker_coro()) asyncio.create_task(mqtt_server.broker_coro())
@ -209,9 +193,6 @@ async def start():
# Start MQTT Helperbot # Start MQTT Helperbot
asyncio.create_task(mqtt_helperbot.start_helper_bot()) asyncio.create_task(mqtt_helperbot.start_helper_bot())
# Start XMPP Server
asyncio.create_task(xmpp_server.start_async_server())
# Wait for helperbot to connect first # Wait for helperbot to connect first
while mqtt_helperbot.Client is None: while mqtt_helperbot.Client is None:
await asyncio.sleep(0.1) await asyncio.sleep(0.1)
@ -248,10 +229,6 @@ async def shutdown():
if mqtt_server.broker.transitions.state == "started": if mqtt_server.broker.transitions.state == "started":
await mqtt_server.broker.shutdown() await mqtt_server.broker.shutdown()
await mqtt_helperbot.Client.disconnect() await mqtt_helperbot.Client.disconnect()
if xmpp_server.server:
if xmpp_server.server._serving:
xmpp_server.server.close()
await xmpp_server.server.wait_closed()
global shutting_down global shutting_down
shutting_down = True shutting_down = True

View file

@ -118,8 +118,8 @@ class ConfServer:
ssl_ctx.load_cert_chain(bumper.server_cert, bumper.server_key) ssl_ctx.load_cert_chain(bumper.server_cert, bumper.server_key)
site = web.TCPSite( site = web.TCPSite(
runner, runner,
host=address, host="0.0.0.0",
port=port, port="443",
ssl_context=ssl_ctx, ssl_context=ssl_ctx,
) )
@ -180,7 +180,6 @@ class ConfServer:
clients = bumper.db_get().table("clients").all() clients = bumper.db_get().table("clients").all()
helperbot = bumper.mqtt_helperbot.Client.session.transitions.state helperbot = bumper.mqtt_helperbot.Client.session.transitions.state
mqttserver = bumper.mqtt_server.broker mqttserver = bumper.mqtt_server.broker
xmppserver = bumper.xmpp_server
mq_sessions = [] mq_sessions = []
for sess in mqttserver._sessions: for sess in mqttserver._sessions:
tmpsess = [] tmpsess = []
@ -203,8 +202,7 @@ class ConfServer:
{"clients": mq_sessions}, {"clients": mq_sessions},
] ]
}, },
], ]
"xmpp_server": xmppserver
} }
resp = aiohttp_jinja2.render_template('home.jinja2', request, context=all) resp = aiohttp_jinja2.render_template('home.jinja2', request, context=all)
#return web.json_response(all) #return web.json_response(all)
@ -335,10 +333,6 @@ class ConfServer:
1.5, lambda: asyncio.create_task(bumper.mqtt_server.broker_coro()) 1.5, lambda: asyncio.create_task(bumper.mqtt_server.broker_coro())
) # In 1.5 seconds start broker ) # In 1.5 seconds start broker
async def restart_XMPP(self):
bumper.xmpp_server.disconnect()
await bumper.xmpp_server.start_async_server()
async def handle_RestartService(self, request): async def handle_RestartService(self, request):
try: try:
service = request.match_info.get("service", "") service = request.match_info.get("service", "")
@ -352,9 +346,6 @@ class ConfServer:
5, lambda: asyncio.create_task(self.restart_Helper()) 5, lambda: asyncio.create_task(self.restart_Helper())
) # In 5 seconds restart Helperbot ) # In 5 seconds restart Helperbot
return web.json_response({"status": "complete"})
elif service == "XMPPServer":
await self.restart_XMPP()
return web.json_response({"status": "complete"}) return web.json_response({"status": "complete"})
else: else:
return web.json_response({"status": "invalid service"}) return web.json_response({"status": "invalid service"})

View file

@ -202,12 +202,6 @@ def user_revoke_authcode(userid, token, authcode):
) )
def get_disconnected_xmpp_clients():
clients = db_get().table("clients")
Client = Query()
return clients.search(Client.xmpp_connection == False)
def check_authcode(uid, authcode): def check_authcode(uid, authcode):
bumperlog.debug("Checking for authcode: {}".format(authcode)) bumperlog.debug("Checking for authcode: {}".format(authcode))
tokens = db_get().table("tokens") tokens = db_get().table("tokens")
@ -328,12 +322,6 @@ def bot_set_mqtt(did, mqtt):
bots.upsert({"mqtt_connection": mqtt}, Bot.did == did) bots.upsert({"mqtt_connection": mqtt}, Bot.did == did)
def bot_set_xmpp(did, xmpp):
bots = db_get().table("bots")
Bot = Query()
bots.upsert({"xmpp_connection": xmpp}, Bot.did == did)
def client_add(userid, realm, resource): def client_add(userid, realm, resource):
newclient = VacBotClient() newclient = VacBotClient()
newclient.userid = userid newclient.userid = userid
@ -368,8 +356,3 @@ def client_set_mqtt(resource, mqtt):
Client = Query() Client = Query()
clients.upsert({"mqtt_connection": mqtt}, Client.resource == resource) clients.upsert({"mqtt_connection": mqtt}, Client.resource == resource)
def client_set_xmpp(resource, xmpp):
clients = db_get().table("clients")
Client = Query()
clients.upsert({"xmpp_connection": xmpp}, Client.resource == resource)

View file

@ -13,7 +13,6 @@ class VacBotDevice(object):
self.nick = nick self.nick = nick
self.resource = resource self.resource = resource
self.mqtt_connection = False self.mqtt_connection = False
self.xmpp_connection = False
def asdict(self): def asdict(self):
return { return {
@ -24,7 +23,6 @@ class VacBotDevice(object):
"nick": self.nick, "nick": self.nick,
"resource": self.resource, "resource": self.resource,
"mqtt_connection": self.mqtt_connection, "mqtt_connection": self.mqtt_connection,
"xmpp_connection": self.xmpp_connection,
} }
@ -52,7 +50,6 @@ class VacBotClient(object):
self.realm = realm self.realm = realm
self.resource = token self.resource = token
self.mqtt_connection = False self.mqtt_connection = False
self.xmpp_connection = False
def asdict(self): def asdict(self):
return { return {
@ -60,7 +57,6 @@ class VacBotClient(object):
"realm": self.realm, "realm": self.realm,
"resource": self.resource, "resource": self.resource,
"mqtt_connection": self.mqtt_connection, "mqtt_connection": self.mqtt_connection,
"xmpp_connection": self.xmpp_connection,
} }

View file

@ -1,161 +0,0 @@
#!/usr/bin/env python3
import asyncio
from aiohttp import web
from bumper import plugins
import logging
import bumper
from bumper.models import *
from bumper import plugins
from datetime import datetime, timedelta
class portal_api_appsvr(plugins.ConfServerApp):
def __init__(self):
self.name = "portal_api_appsvr"
self.plugin_type = "sub_api"
self.sub_api = "portal_api"
self.routes = [
web.route("*", "/appsvr/app.do", self.handle_appsvr_api, name="portal_api_appsvr_app"),
]
self.get_milli_time = bumper.ConfServer.ConfServer_GeneralFunctions().get_milli_time
async def handle_appsvr_api(self, request):
if not request.method == "GET": # Skip GET for now
try:
body = {}
postbody = {}
if request.content_type == "application/x-www-form-urlencoded":
postbody = await request.post()
else:
postbody = json.loads(await request.text())
todo = postbody["todo"]
if todo == "GetGlobalDeviceList": # EcoVacs Home
bots = bumper.db_get().table("bots").all()
botlist = []
for bot in bots:
if bot["class"] != "":
b = bumper.bot_toEcoVacsHome_JSON(bot)
if (
not b is None
): # Happens if the bot isn't on the EcoVacs Home list
botlist.append(json.loads(b))
body = {
"code": 0,
"devices": botlist,
"ret": "ok",
"todo": "result",
}
return web.json_response(body)
#elif todo == "GetShareDeviceList":
# example response
# {
# "code": 0,
# "devices": [
# {
# "deviceName": "DEEBOT 900 Series (Cleaner Cleaner)",
# "did": "did",
# "icon": "https://portal-ww.ecouser.net/api/pim/file/get/5ba4a2cb6c2f120001c32839",
# "mid": "ls1ok3",
# "ownUsers": {
# "isMe": true,
# "nickname": "user@gmail.com",
# "user": "cg****"
# },
# "resource": "wC3g",
# "share": true,
# "shareUsers": []
# }
# ],
# "ret": "ok",
# "todo": "result"
# }
# if shared shareUsers
# "shareUsers": [
# {
# "isMe": false,
# "nickname": "user@gmail.com",
# "status": "sharing",
# "user": "eafg****"
# }
# ]
#elif todo == "ShareDevice":
# example post
# {
# "todo": "ShareDevice",
# "accountType": "EMAIL",
# "auth": {
# "realm": "ecouser.net",
# "resource": "res",
# "token": "token***",
# "userid": "cg***",
# "with": "users"
# },
# "country": "US",
# "did": "did",
# "resource": "wC3g",
# "username": "email to share to"
# }
#fail response (no user)
# {
# "todo": "result",
# "code": -3,
# "errno": -3,
# "ret": "fail"
# }
#success response
#{"ret":"ok","code":0,"todo":"result"}
#elif todo == "ShareUnRegisterDevice":
# example post
# {
# "todo": "ShareUnRegisterDevice",
# "account": "email to share to",
# "auth": {
# "realm": "ecouser.net",
# "resource": "res",
# "token": "toke",
# "userid": "userid",
# "with": "users"
# },
# "country": "US",
# "did": "did",
# "lang": "EN",
# "mid": "ls1ok3"
# }
# example response
# {
# "todo": "result",
# "code": 0,
# "data": {
# "mailContent": "<!-- 邮件代码 --><div style=\"position: relative;\"><div style=\"font-size: 14px;font-family: Helvetica;padding: 65px 0;line-height: 150%;\"><p style=\"margin-bottom: 15px;\">Hey there,</p><p style=\"margin-bottom: 15px;\">\n\t\t\tCheck out my new awesome robot vacuum: <a href=\"#\" style=\"color: #1c95ea;text-decoration: none;\">DEEBOT 900 Series</a>!<br/>\n\t\t\tDownload the <strong>ECOVACS HOME</strong> App and sign up with your email: <a href=\"#\" style=\"color: #1c95ea;text-decoration: none;\">brian@bmartin.net</a>, so you and I can control this kick-ass robot together.</p><p style=\"margin-bottom: 15px;\">\n\t\t\tIOS: <span style=\"font-size:15px\"><a href=\"https://itunes.apple.com/us/app/ecovacs-home/id1329458504?l=zh&ls=1&mt=8\">https://itunes.apple.com/us/app/ecovacs-home/id1329458504?l=zh&amp;ls=1&amp;mt=8</a></span><br/>\n\t\t\tAndroid: <span style=\"font-size:15px\"><a href=\"https://play.google.com/store/apps/details?id=com.eco.global.app\">https://play.google.com/store/apps/details?id=com.eco.global.app</a></span></p><p style=\"margin-bottom: 15px;\">\n\t\t\tThis invitation is valid within 7 days.<br/>\n\t\t\tIf I&#39;m sending to the wrong person, please ignore this email.</p><p style=\"margin-bottom: 15px;\">Thank you.</p></div></div><!-- 邮件代码 -->",
# "mailTitle": "I'm sharing my DEEBOT and you're invited!"
# },
# "ret": "ok"
# }
except Exception as e:
logging.exception("{}".format(e))
# Return fail for GET
body = {"result": "fail", "todo": "result"}
return web.json_response(body)
plugin = portal_api_appsvr()

View file

@ -1,40 +0,0 @@
#!/usr/bin/env python3
import asyncio
from aiohttp import web
from bumper import plugins
import logging
import bumper
from bumper.models import *
from bumper import plugins
from datetime import datetime, timedelta
import os
class upload_global(plugins.ConfServerApp):
def __init__(self):
self.name = "upload_global"
self.plugin_type = "sub_api"
self.sub_api = "upload_api"
self.routes = [
web.route("*", "/global/{year}/{month}/{day}/{fileid}", self.handle_upload_global_file, name="upload_global_getFile"),
]
self.get_milli_time = bumper.ConfServer.ConfServer_GeneralFunctions().get_milli_time
async def handle_upload_global_file(self, request):
try:
fileID = request.match_info.get("id", "")
return web.FileResponse(os.path.join(bumper.bumper_dir,"bumper","web","images","robotvac_image.jpg"))
except Exception as e:
logging.exception("{}".format(e))
plugin = upload_global()

View file

@ -1,58 +0,0 @@
#!/usr/bin/env python3
import asyncio
from aiohttp import web
from bumper import plugins
import logging
import bumper
from bumper.models import *
from bumper import plugins
from datetime import datetime, timedelta
class v1_private_ad(plugins.ConfServerApp):
def __init__(self):
self.name = "v1_private_ad"
self.plugin_type = "sub_api"
self.sub_api = "api_v1"
self.routes = [
web.route("*", "/private/{country}/{language}/{devid}/{apptype}/{appversion}/{devtype}/{aid}/ad/getAdByPositionType", self.handle_getAdByPositionType, name="v1_ad_getAdByPositionType"),
web.route("*", "/private/{country}/{language}/{devid}/{apptype}/{appversion}/{devtype}/{aid}/ad/getBootScreen", self.handle_getBootScreen, name="v1_ad_getBootScreen"),
]
self.get_milli_time = bumper.ConfServer.ConfServer_GeneralFunctions().get_milli_time
async def handle_getAdByPositionType(self, request): # EcoVacs Home
try:
body = {
"code": bumper.RETURN_API_SUCCESS,
"data": None,
"msg": "操作成功",
"success": True,
"time": self.get_milli_time(datetime.utcnow().timestamp()),
}
return web.json_response(body)
except Exception as e:
logging.exception("{}".format(e))
async def handle_getBootScreen(self, request): # EcoVacs Home
try:
body = {
"code": bumper.RETURN_API_SUCCESS,
"data": None,
"msg": "操作成功",
"success": True,
"time": self.get_milli_time(datetime.utcnow().timestamp()),
}
return web.json_response(body)
except Exception as e:
logging.exception("{}".format(e))
plugin = v1_private_ad()

View file

@ -1,54 +0,0 @@
#!/usr/bin/env python3
import asyncio
from aiohttp import web
from bumper import plugins
import logging
import bumper
from bumper.models import *
from bumper import plugins
from datetime import datetime, timedelta
class v1_private_campaign(plugins.ConfServerApp):
def __init__(self):
self.name = "v1_private_campaign"
self.plugin_type = "sub_api"
self.sub_api = "api_v1"
self.routes = [
web.route("*", "/private/{country}/{language}/{devid}/{apptype}/{appversion}/{devtype}/{aid}/campaign/homePageAlert", self.handle_homePageAlert, name="v1_campaign_homePageAlert"),
]
self.get_milli_time = bumper.ConfServer.ConfServer_GeneralFunctions().get_milli_time
async def handle_homePageAlert(self, request):
try:
nextAlert = self.get_milli_time(
(datetime.now() + timedelta(hours=12)).timestamp()
)
body = {
"code": bumper.RETURN_API_SUCCESS,
"data": {
"clickSchemeUrl": None,
"clickWebUrl": None,
"hasCampaign": "N",
"imageUrl": None,
"nextAlertTime": nextAlert,
"serverTime": self.get_milli_time(datetime.utcnow().timestamp()),
},
"msg": "操作成功",
"time": self.get_milli_time(datetime.utcnow().timestamp()),
}
return web.json_response(body)
except Exception as e:
logging.exception("{}".format(e))
plugin = v1_private_campaign()

View file

@ -1,48 +0,0 @@
#!/usr/bin/env python3
import asyncio
from aiohttp import web
from bumper import plugins
import logging
import bumper
from bumper.models import *
from bumper import plugins
from datetime import datetime, timedelta
class v1_private_shop(plugins.ConfServerApp):
def __init__(self):
self.name = "v1_private_shop"
self.plugin_type = "sub_api"
self.sub_api = "api_v1"
self.routes = [
web.route("*", "/private/{country}/{language}/{devid}/{apptype}/{appversion}/{devtype}/{aid}/shop/getCnWapShopConfig", self.handle_getCnWapShopConfig, name="v1_shop_getCnWapShopConfig"),
]
self.get_milli_time = bumper.ConfServer.ConfServer_GeneralFunctions().get_milli_time
async def handle_getCnWapShopConfig(self, request): # EcoVacs Home
try:
body = {
"code": bumper.RETURN_API_SUCCESS,
"data": {
"myShopShowFlag": "N",
"myShopUrl": "",
"shopIndexShowFlag": "N",
"shopIndexUrl": "",
},
"msg": "操作成功",
"success": True,
"time": self.get_milli_time(datetime.utcnow().timestamp()),
}
return web.json_response(body)
except Exception as e:
logging.exception("{}".format(e))
plugin = v1_private_shop()

View file

@ -1,29 +0,0 @@
#!/usr/bin/env python3
import asyncio
from aiohttp import web
from bumper import plugins
import logging
import bumper
from bumper.models import *
from bumper import plugins
from datetime import datetime, timedelta
class v2_private_user(plugins.ConfServerApp):
def __init__(self):
self.name = "v2_private_user"
self.plugin_type = "sub_api"
self.sub_api = "api_v2"
authhandler = bumper.ConfServer.ConfServer_AuthHandler()
self.routes = [
web.route("*", "/private/{country}/{language}/{devid}/{apptype}/{appversion}/{devtype}/{aid}/user/checkLogin", authhandler.login, name="v2_user_checkLogin"),
]
self.get_milli_time = bumper.ConfServer.ConfServer_GeneralFunctions().get_milli_time
plugin = v2_private_user()

View file

@ -92,33 +92,6 @@
</br> </br>
<div class="card border border-dark">
<div class="card card-header">
<h3 class="card-title">XMPP Server</h3>
<div>Action: <button type="button" class="btn btn-outline-danger btn-sm" onclick="restartService('XMPPServer');">Restart Service</button></div>
</div>
<div class="card-body">
Status: {% if xmpp_server.server._serving == True %} <span class="badge badge-success">running</span> {% else %} <span class="badge badge-danger">not running</span> {% endif %}
Clients: {{ xmpp_server.clients | length }}
<table class="table table-striped table-bordered table-responsive-lg">
<thead class="thead-dark">
<TH>uid</TH><TH>jid</TH><TH>state</TH>
</thead>
{% for client in xmpp_server.clients %}
<TR>
<TD>{{ client.uid }}</TD>
<TD>{{ client.bumper_jid }}</TD>
<TD {% if client.state == 4 %} class="table-success" {% endif %}>{% if client.state == 4 %} connected {% else %} not connected {% endif %}</TD>
</TR>
{% endfor %}
</table>
</div>
</div>
</br>
<div class="card border border-dark"> <div class="card border border-dark">
<div class="card card-header"> <div class="card card-header">
<h3 class="card-title">Helperbot</h3> <h3 class="card-title">Helperbot</h3>
@ -145,7 +118,7 @@
<div class="card-body"> <div class="card-body">
<table class="table table-striped table-bordered table-responsive-lg"> <table class="table table-striped table-bordered table-responsive-lg">
<thead class="thead-dark"> <thead class="thead-dark">
<TH>SN</TH><TH>Nickname</TH><TH>Class</TH><TH>DID</TH><TH>Resource</TH><TH>Company</TH><TH>MQTT Connected</TH><TH>XMPP Connected</TH><TH>Action</TH> <TH>SN</TH><TH>Nickname</TH><TH>Class</TH><TH>DID</TH><TH>Resource</TH><TH>Company</TH><TH>MQTT Connected</TH><TH>Action</TH>
</thead> </thead>
{% for bot in bots %} {% for bot in bots %}
<TR> <TR>
@ -157,7 +130,6 @@
<TD>{{ bot.company}} </TD> <TD>{{ bot.company}} </TD>
<TD {% if bot.mqtt_connection == True %} class="table-success" {% endif %}> {{ bot.mqtt_connection }} </TD> <TD {% if bot.mqtt_connection == True %} class="table-success" {% endif %}> {{ bot.mqtt_connection }} </TD>
<TD {% if bot.xmpp_connection == True %} class="table-success" {% endif %}> {{ bot.xmpp_connection }}</TD>
<TD> <button type="button" class="btn btn-outline-danger btn-sm" onclick="removeBot('{{ bot.did }}');">Remove</button> </TD> <TD> <button type="button" class="btn btn-outline-danger btn-sm" onclick="removeBot('{{ bot.did }}');">Remove</button> </TD>
</TR> </TR>
{% endfor %} {% endfor %}
@ -175,7 +147,7 @@
<div class="card-body"> <div class="card-body">
<table class="table table-striped table-bordered table-responsive-lg"> <table class="table table-striped table-bordered table-responsive-lg">
<thead class="thead-dark"> <thead class="thead-dark">
<TH>User ID</TH><TH>Realm</TH><TH>Resource</TH><TH>MQTT Connected</TH><TH>XMPP Connected</TH><TH>Action</TH> <TH>User ID</TH><TH>Realm</TH><TH>Resource</TH><TH>MQTT Connected</TH><TH>Action</TH>
</thead> </thead>
{% for client in clients %} {% for client in clients %}
<TR> <TR>
@ -183,7 +155,6 @@
<TD>{{ client.realm }}</TD> <TD>{{ client.realm }}</TD>
<TD>{{ client.resource }}</TD> <TD>{{ client.resource }}</TD>
<TD {% if client.mqtt_connection == True %} class="table-success" {% endif %}> {{ client.mqtt_connection }} </TD> <TD {% if client.mqtt_connection == True %} class="table-success" {% endif %}> {{ client.mqtt_connection }} </TD>
<TD {% if client.xmpp_connection == True %} class="table-success" {% endif %}> {{ client.xmpp_connection }}</TD>
<TD> <button type="button" class="btn btn-outline-danger btn-sm" onclick="removeClient('{{ client.resource }}');">Remove</button> </TD> <TD> <button type="button" class="btn btn-outline-danger btn-sm" onclick="removeClient('{{ client.resource }}');">Remove</button> </TD>
</TR> </TR>
{% endfor %} {% endfor %}

View file

@ -1,827 +0,0 @@
#!/usr/bin/env python3
import logging
import re
import uuid
import xml.etree.ElementTree as ET
import base64
import ssl
import bumper
import asyncio
xmppserverlog = logging.getLogger("xmppserver")
boterrorlog = logging.getLogger("boterror")
class XMPPServer:
server_id = "ecouser.net"
clients = []
exit_flag = False
server = None
def __init__(self, address):
# Initialize bot server
self.address = address
self.xmpp_protocol = lambda: XMPPServer_Protocol()
async def start_async_server(self):
try:
xmppserverlog.info(
"Starting XMPP Server at {}:{}".format(self.address[0], self.address[1])
)
loop = asyncio.get_running_loop()
self.server = await loop.create_server(
self.xmpp_protocol, host=self.address[0], port=self.address[1]
)
self.server_coro = loop.create_task(self.server.serve_forever())
except PermissionError as e:
xmppserverlog.error(e.strerror)
asyncio.create_task(bumper.shutdown())
pass
except asyncio.CancelledError:
pass
except Exception as e:
xmppserverlog.exception("{}".format(e))
asyncio.create_task(bumper.shutdown())
def disconnect(self):
xmppserverlog.debug("waiting for all clients to disconnect")
for client in self.clients:
client._disconnect()
self.exit_flag = True
xmppserverlog.debug("shutting down")
self.server_coro.cancel()
class XMPPServer_Protocol(asyncio.Protocol):
client_id = None
exit_flag = False
aclient = None
def connection_made(self, transport):
if self.aclient: # Existing client... upgrading to TLS
xmppserverlog.debug(
"Upgraded connection for {}".format(self.aclient.address)
)
self.aclient.transport = transport
else:
aclient = XMPPAsyncClient(transport)
self.aclient = aclient
XMPPServer.clients.append(aclient)
self.aclient.state = getattr(aclient, "CONNECT")
xmppserverlog.debug("New Connection from {}".format(aclient.address))
def connection_lost(self, error):
XMPPServer.clients.remove(self.aclient)
self.aclient._set_state("DISCONNECT")
xmppserverlog.debug(
"End Connection for ({}:{} | {})".format(
self.aclient.address[0],
self.aclient.address[1],
self.aclient.bumper_jid,
)
)
def data_received(self, data):
self.aclient._parse_data(data)
class XMPPAsyncClient:
IDLE = 0
CONNECT = 1
INIT = 2
BIND = 3
READY = 4
DISCONNECT = 5
UNKNOWN = 0
BOT = 1
CONTROLLER = 2
TLSUpgraded = False
def __init__(self, transport):
self.type = self.UNKNOWN
self.state = self.IDLE
self.address = transport.get_extra_info("peername")
self.transport = transport
self.clientresource = ""
self.devclass = ""
self.bumper_jid = ""
self.uid = ""
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 with ip {}".format(self.address))
def send(self, command):
try:
if self.log_sent_message:
xmppserverlog.debug(
"send to ({}:{} | {}) - {}".format(
self.address[0], self.address[1], self.bumper_jid, command
)
)
self.transport.write(command.encode())
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.transport.close()
except Exception as e:
xmppserverlog.error("{}".format(e))
def _tag_strip_uri(self, tag):
try:
if tag[0] == "{":
_, _, tag = tag[1:].partition("}")
return tag
except Exception as e:
xmppserverlog.error("{}".format(e))
def _set_state(self, state):
try:
new_state = getattr(XMPPAsyncClient, state)
if self.state > new_state:
raise Exception(
"{} illegal state change {}->{}".format(
self.address, self.state, new_state
)
)
xmppserverlog.debug(
"({}:{} | {}) state: {}".format(
self.address[0], self.address[1], self.bumper_jid, state
)
)
self.state = new_state
if new_state == 5:
self._disconnect()
except Exception as e:
xmppserverlog.error("{}".format(e))
def _handle_ctl(self, xml, data):
try:
if "roster" in data:
# Return not-implemented for roster
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(
xml.get("id")
)
)
return
if "disco#items" in data:
# Return not-implemented for disco#items
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(
xml.get("id")
)
)
return
if "disco#info" in data:
# Return not-implemented for disco#info
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(
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(
'<iq id="{}" to="{}@{}/{}" from="rl.ecorobot.net" type="result"/>'.format(
xml.get("id"),
self.uid,
XMPPServer.server_id,
self.clientresource,
)
)
if len(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")
if not "from" in xml.attrib:
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("<query", '<query xmlns="com:ctl"')
if client.type == self.BOT:
if client.uid.lower() in ctl_to.lower():
xmppserverlog.debug(
"Sending ctl to bot: {}".format(rxmlstring)
)
client.send(rxmlstring)
except Exception as e:
xmppserverlog.error("{}".format(e))
def _handle_ping(self, xml, data):
try:
if xml.get("to").find("@") == -1: # No to address
# Ping to server - respond
pingresp = '<iq type="result" id="{}" from="{}" />'.format(
xml.get("id"), xml.get("to")
)
# xmppserverlog.debug("Server Ping resp: {}".format(pingresp))
self.send(pingresp)
else:
pingto = xml.get("to")
pingfrom = self.bumper_jid
if not "from" in xml.attrib:
xml.attrib["from"] = "{}".format(pingfrom)
pingstring = ET.tostring(xml).decode("utf-8")
# clean up string to remove namespaces added by ET
pingstring = pingstring.replace("xmlns:ns0=", "xmlns=")
pingstring = pingstring.replace("ns0:", "")
pingstring = pingstring.replace('iq xmlns="urn:xmpp:ping"', "iq")
pingstring = pingstring.replace("<ping", '<ping xmlns="urn:xmpp:ping"')
for client in XMPPServer.clients:
if (
client.bumper_jid != self.bumper_jid
and client.state == client.READY
):
if client.uid.lower() in pingto.lower():
client.send(pingstring)
except Exception as e:
xmppserverlog.exception("{}".format(e))
async def schedule_ping(self, time):
if not self.state == 5: # disconnected
pingstring = "<iq from='{}' to='{}' id='s2c1' type='get'><ping xmlns='urn:xmpp:ping'/></iq>".format(
XMPPServer.server_id, self.bumper_jid
)
self.send(pingstring)
await asyncio.sleep(time)
asyncio.Task(self.schedule_ping(time))
def _handle_result(self, xml, data):
try:
ctl_to = xml.get("to")
if not "from" in xml.attrib:
xml.attrib["from"] = "{}".format(self.bumper_jid)
if "errno" in data:
xmppserverlog.error(f"Error from bot - {data}")
if (
"errno='103'" in data
): # No permissions, usually if bot was last on Ecovac network, Bumper will try to add fuid user as owner
if self.type == self.BOT:
xmppserverlog.info("Bot reported user has no permissions, Bumper will attempt to add user to bot. This is typical if bot was last on Ecovacs Network.")
xquery = xml.getchildren()
ctl = xquery[0].getchildren()
if "error" in ctl[0].attrib:
ctlerr = ctl[0].attrib["error"]
adminuser = ctlerr.replace("permission denied, please contact ", "")
adminuser = adminuser.replace(" ", "")
elif "admin" in ctl[0].attrib:
adminuser = ctl[0].attrib["admin"]
if not (
adminuser.startswith("fuid_")
or adminuser.startswith("fusername_")
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("Adding User to bot - {}".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 to bot - {}".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
)
)
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
ctl_to = xml.get("to")
else:
ctl_to = "{}@ecouser.net".format(ctl_to.split("@")[0])
for client in XMPPServer.clients:
if (
client.bumper_jid != self.bumper_jid
and client.state == client.READY
):
if not "@" in ctl_to: # No user@, send to all clients?
# TODO: Revisit later, this may be wrong
client.send(rxmlstring)
elif (
client.uid.lower() in ctl_to.lower()
): # If client matches TO=
xmppserverlog.debug(
"Sending from {} to client {}: {}".format(
self.uid, client.uid, rxmlstring
)
)
client.send(rxmlstring)
except Exception as e:
xmppserverlog.exception("{}".format(e))
def _handle_connect(self, data, xml=None):
try:
if self.state == self.CONNECT:
if xml == None:
# Client first connecting, send our features
if data.decode("utf-8").find("jabber:client") > -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
# Send stream tag to client, acknowledging connection
self.send(
'<stream:stream xmlns:stream="http://etherx.jabber.org/streams" xmlns="jabber:client" version="1.0" id="1" from="{}">'.format(
XMPPServer.server_id
)
)
# Send STARTTLS to client with auth mechanisms
if self.TLSUpgraded == False:
# With STARTTLS #https://xmpp.org/rfcs/rfc3920.html
self.send(
'<stream:features><starttls xmlns="urn:ietf:params:xml:ns:xmpp-tls"><required/></starttls><mechanisms xmlns="urn:ietf:params:xml:ns:xmpp-sasl"><mechanism>PLAIN</mechanism></mechanisms></stream:features>'
)
else:
# Already using TLS send authentication support for SASL
self.send(
'<stream:features><mechanisms xmlns="urn:ietf:params:xml:ns:xmpp-sasl"><mechanism>PLAIN</mechanism></mechanisms></stream:features>'
)
else:
self.send("</stream>")
else:
if (
"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(
'<stream:stream xmlns:stream="http://etherx.jabber.org/streams" xmlns="jabber:client" version="1.0" id="1" from="{}">'.format(
XMPPServer.server_id
)
)
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
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))
async def _handle_starttls(self, data):
try:
if self.TLSUpgraded == False:
self.TLSUpgraded = (
True
) # Set TLSUpgraded true to prevent further attempts to upgrade connection
xmppserverlog.debug(
"Upgrading connection with STARTTLS for {}:{}".format(
self.address[0], self.address[1]
)
)
self.send(
"<proceed xmlns='urn:ietf:params:xml:ns:xmpp-tls'/>"
) # send process to client
# After proceed the connection should be upgraded to TLS
loop = asyncio.get_event_loop()
transport = self.transport
protocol = self.transport.get_protocol()
ssl_ctx = ssl.create_default_context(ssl.Purpose.CLIENT_AUTH)
ssl_ctx.load_cert_chain(bumper.server_cert, bumper.server_key)
ssl_ctx.load_verify_locations(cafile=bumper.ca_cert)
new_transport = await loop.start_tls(
transport, protocol, ssl_ctx, server_side=True
)
protocol.connection_made(new_transport)
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]
authcode = ""
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 self.devclass: # if there is a devclass it is a bot
bumper.bot_add(self.uid, self.uid, self.devclass, "atom", "eco-legacy")
self.type = self.BOT
xmppserverlog.info("bot authenticated SN: {}".format(self.uid))
# Send response
self.send(
'<success xmlns="urn:ietf:params:xml:ns:xmpp-sasl"/>'
) # 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.info("client authenticated {}".format(self.uid))
# Client authenticated, move to next state
self._set_state("INIT")
# Send response
self.send(
'<success xmlns="urn:ietf:params:xml:ns:xmpp-sasl"/>'
) # Success
else:
# Failed to authenticate
self.send(
'<response xmlns="urn:ietf:params:xml:ns:xmpp-sasl"/>'
) # 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.address[0], self.address[1], self.bumper_jid
)
)
res = '<iq type="result" id="{}"><bind xmlns="urn:ietf:params:xml:ns:xmpp-bind"><jid>{}</jid></bind></iq>'.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 ({}:{} | {})".format(
self.address[0], self.address[1], self.bumper_jid
)
)
res = '<iq type="result" id="{}"><bind xmlns="urn:ietf:params:xml:ns:xmpp-bind"><jid>{}</jid></bind></iq>'.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.address[0], self.address[1], self.bumper_jid
)
)
res = '<iq type="result" id="{}"><bind xmlns="urn:ietf:params:xml:ns:xmpp-bind"><jid>{}</jid></bind></iq>'.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):
res = '<iq type="result" id="{}" />'.format(xml.get("id"))
self._set_state("READY")
self.send(res)
asyncio.Task(self.schedule_ping(30))
def _handle_presence(self, xml):
if len(xml) and xml[0].tag == "status":
xmppserverlog.debug(
"bot presence {} ".format(
ET.tostring(xml, encoding="utf-8").decode("utf-8")
)
)
# Most likely a bot, possibly hello world in text
# Send dummy return
self.send('<presence to="{}"> dummy </presence>'.format(self.bumper_jid))
# If it is a BOT, send extras
if self.type == self.BOT:
# 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:
xmppserverlog.debug(
"client presence - {} ".format(
ET.tostring(xml, encoding="utf-8").decode("utf-8")
)
)
if xml.get("type") == "available":
xmppserverlog.debug(
"client presence available - {} ".format(
ET.tostring(xml, encoding="utf-8").decode("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").decode("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)
)
def _parse_data(self, data):
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:
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[0],
self.address[1],
self.bumper_jid,
str(
ET.tostring(item, encoding="utf-8").decode(
"utf-8"
)
).replace("ns0:", ""),
)
)
if (
'td="error"' in newdata
or "errs=" in newdata
or 'k="DeviceAlert' in newdata
):
boterrorlog.error(
"Received Error from ({}:{} | {}) - {}".format(
self.address[0],
self.address[1],
self.bumper_jid,
newdata,
)
)
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 "-tls" in item.tag:
if not self.TLSUpgraded:
asyncio.Task(self._handle_starttls(newdata.encode("utf-8")))
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 "<stream:stream " in newdata:
if self.state == self.CONNECT or self.state == self.INIT:
self._handle_connect(newdata.encode("utf-8"))
else:
if not (newdata == "" or newdata == " "):
xmppserverlog.error(
"xml parse error - {} - {}".format(newdata, e)
)
elif "not well-formed (invalid token)" in e.msg:
# If a lone </stream:stream> - client is signalling end of session/disconnect
if not "</stream:stream>" in newdata:
xmppserverlog.error("xml parse error - {} - {}".format(newdata, e))
else:
self.send("</stream:stream>") # Close stream
else:
if "<stream:stream" in newdata: # Handle start stream and connect
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:
xmppserverlog.exception("{}".format(e))
def _handle_iq(self, xml, data):
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)

View file

@ -94,12 +94,6 @@ async def test_base(aiohttp_client):
bumper.mqtt_server = mqtt_server bumper.mqtt_server = mqtt_server
await mqtt_server.broker_coro() await mqtt_server.broker_coro()
# Start XMPP
xmpp_address = ("127.0.0.1", 5223)
xmpp_server = bumper.XMPPServer(xmpp_address)
bumper.xmpp_server = xmpp_server
await xmpp_server.start_async_server()
# Start Helperbot # Start Helperbot
mqtt_helperbot = bumper.MQTTHelperBot(mqtt_address) mqtt_helperbot = bumper.MQTTHelperBot(mqtt_address)
bumper.mqtt_helperbot = mqtt_helperbot bumper.mqtt_helperbot = mqtt_helperbot
@ -113,8 +107,6 @@ async def test_base(aiohttp_client):
await mqtt_server.broker.shutdown() await mqtt_server.broker.shutdown()
bumper.xmpp_server.disconnect()
async def test_restartService(aiohttp_client): async def test_restartService(aiohttp_client):
remove_existing_db() remove_existing_db()
@ -126,12 +118,6 @@ async def test_restartService(aiohttp_client):
bumper.mqtt_server = mqtt_server bumper.mqtt_server = mqtt_server
await mqtt_server.broker_coro() await mqtt_server.broker_coro()
# Start XMPP
xmpp_address = ("127.0.0.1", 5223)
xmpp_server = bumper.XMPPServer(xmpp_address)
bumper.xmpp_server = xmpp_server
await xmpp_server.start_async_server()
# Start Helperbot # Start Helperbot
mqtt_helperbot = bumper.MQTTHelperBot(mqtt_address) mqtt_helperbot = bumper.MQTTHelperBot(mqtt_address)
bumper.mqtt_helperbot = mqtt_helperbot bumper.mqtt_helperbot = mqtt_helperbot
@ -145,14 +131,9 @@ async def test_restartService(aiohttp_client):
resp = await client.get("/restart_MQTTServer") resp = await client.get("/restart_MQTTServer")
assert resp.status == 200 assert resp.status == 200
resp = await client.get("/restart_XMPPServer")
assert resp.status == 200
mqtt_helperbot.Client.disconnect() mqtt_helperbot.Client.disconnect()
await mqtt_server.broker.shutdown() await mqtt_server.broker.shutdown()
xmpp_server.disconnect()
async def test_RemoveBot(aiohttp_client): async def test_RemoveBot(aiohttp_client):
client = await aiohttp_client(create_app) client = await aiohttp_client(create_app)
resp = await client.get("/bot/remove/test_did") resp = await client.get("/bot/remove/test_did")

View file

@ -117,11 +117,6 @@ def test_bot_db():
"mqtt_connection" "mqtt_connection"
] # Test that mqtt was set True for bot ] # Test that mqtt was set True for bot
bumper.bot_set_xmpp("did_123", True)
assert bumper.bot_get("did_123")[
"xmpp_connection"
] # Test that xmpp was set True for bot
bumper.bot_remove("did_123") bumper.bot_remove("did_123")
assert bumper.bot_get("did_123") == None # Test that bot is no longer in db assert bumper.bot_get("did_123") == None # Test that bot is no longer in db
@ -136,13 +131,5 @@ def test_client_db():
"mqtt_connection" "mqtt_connection"
] # Test that mqtt was set True for client ] # Test that mqtt was set True for client
bumper.client_set_xmpp("resource_123", False)
assert (
bumper.client_get("resource_123")["xmpp_connection"] == False
) # Test that xmpp was set False for client
assert (
len(bumper.get_disconnected_xmpp_clients()) > 0
) # Test len of connected xmpp clients is 1
bumper.client_remove("resource_123") bumper.client_remove("resource_123")
assert bumper.client_get("resource_123") == None assert bumper.client_get("resource_123") == None

View file

@ -1,645 +0,0 @@
import mock
import bumper
import asyncio
import pytest
import os
import json
import tinydb
import pytest_asyncio
import xml.etree.ElementTree as ET
import socket
from testfixtures import LogCapture
import ssl
def return_send_data(data, *args, **kwargs):
return data
def mock_transport_extra_info(*args, **kwargs):
return ("127.0.0.1", 5223)
async def test_xmpp_server():
xmpp_address = ("127.0.0.1", 5223)
xmpp_server = bumper.XMPPServer(xmpp_address)
await xmpp_server.start_async_server()
with LogCapture("xmppserver") as l:
reader, writer = await asyncio.open_connection("127.0.0.1", 5223)
writer.write(b"<stream:stream />") # Start stream
await writer.drain()
await asyncio.sleep(0.1)
assert len(xmpp_server.clients) == 1 # Client count increased
assert (
xmpp_server.clients[0].address[1]
== writer.transport.get_extra_info("sockname")[1]
)
writer.close() # Close connection
await writer.wait_closed()
await asyncio.sleep(0.1)
assert len(xmpp_server.clients) == 0 # Client count decreased
reader, writer = await asyncio.open_connection("127.0.0.1", 5223)
writer.write(b"<stream:stream />") # Start stream
await writer.drain()
await asyncio.sleep(0.1)
xmpp_server.disconnect()
async def test_client_connect_no_starttls(*args, **kwargs):
test_transport = asyncio.Transport()
test_transport.get_extra_info = mock.Mock(return_value=mock_transport_extra_info())
test_transport.write = mock.Mock(return_value=return_send_data)
xmppclient = bumper.xmppserver.XMPPAsyncClient(test_transport)
xmppclient.state = xmppclient.CONNECT # Set client state to CONNECT
mock_send = xmppclient.send = mock.Mock(side_effect=return_send_data)
# Send connect stream from "client"
test_data = "<stream:stream xmlns='jabber:client' xmlns:stream='http://etherx.jabber.org/streams' version='1.0' to='ecouser.net'>".encode(
"utf-8"
)
xmppclient._parse_data(test_data)
# Expect 2 calls to send
assert mock_send.call_count == 2
# Server opens stream
assert (
mock_send.mock_calls[0].args[0]
== '<stream:stream xmlns:stream="http://etherx.jabber.org/streams" xmlns="jabber:client" version="1.0" id="1" from="ecouser.net">'
)
# Server tells client available features
assert (
mock_send.mock_calls[1].args[0]
== '<stream:features><starttls xmlns="urn:ietf:params:xml:ns:xmpp-tls"><required/></starttls><mechanisms xmlns="urn:ietf:params:xml:ns:xmpp-sasl"><mechanism>PLAIN</mechanism></mechanisms></stream:features>'
)
# Reset mock calls
mock_send.reset_mock()
# Client sendss auth - Ignoring the starttls, we don't force this with bumper
test_data = '<auth xmlns="urn:ietf:params:xml:ns:xmpp-sasl" mechanism="PLAIN">AGZ1aWRfdG1wdXNlcgAwL0lPU0Y1M0QwN0JBL3VzXzg5ODgwMmZkYmM0NDQxYjBiYzgxNWIxZDFjNjgzMDJl</auth>'.encode(
"utf-8"
)
xmppclient._parse_data(test_data)
assert (
mock_send.mock_calls[0].args[0]
== '<success xmlns="urn:ietf:params:xml:ns:xmpp-sasl"/>'
) # Client successfully authenticated
assert xmppclient.state == xmppclient.INIT # Client moved to INIT state
async def test_client_end_stream(*args, **kwargs):
test_transport = asyncio.Transport()
test_transport.get_extra_info = mock.Mock(return_value=mock_transport_extra_info())
test_transport.write = mock.Mock(return_value=return_send_data)
xmppclient = bumper.xmppserver.XMPPAsyncClient(test_transport)
xmppclient.state = xmppclient.CONNECT # Set client state to CONNECT
mock_send = xmppclient.send = mock.Mock(side_effect=return_send_data)
# Send end stream from "client"
test_data = "</stream:stream>".encode("utf-8")
xmppclient._parse_data(test_data)
# Expect 2 calls to send
assert mock_send.call_count == 1
# Server opens stream
assert mock_send.mock_calls[0].args[0] == "</stream:stream>"
# Reset mock calls
mock_send.reset_mock()
# Send abnormal stream from "client"
test_data = "<badstr />".encode("utf-8")
xmppclient._parse_data(test_data)
# Reset mock calls
mock_send.reset_mock()
# Send blank from "client"
test_data = "".encode("utf-8")
xmppclient._parse_data(test_data)
async def test_client_connect_starttls_called(*args, **kwargs):
test_transport = asyncio.Transport()
test_transport.get_extra_info = mock.Mock(return_value=mock_transport_extra_info())
test_transport.write = mock.Mock(return_value=return_send_data)
xmppclient = bumper.xmppserver.XMPPAsyncClient(test_transport)
xmppclient.state = xmppclient.CONNECT # Set client state to CONNECT
mock_send = xmppclient.send = mock.Mock(side_effect=return_send_data)
# Send connect stream from "client"
test_data = "<stream:stream xmlns='jabber:client' xmlns:stream='http://etherx.jabber.org/streams' version='1.0' to='ecouser.net'>".encode(
"utf-8"
)
xmppclient._parse_data(test_data)
# Expect 2 calls to send
assert mock_send.call_count == 2
# Server opens stream
assert (
mock_send.mock_calls[0].args[0]
== '<stream:stream xmlns:stream="http://etherx.jabber.org/streams" xmlns="jabber:client" version="1.0" id="1" from="ecouser.net">'
)
# Server tells client available features
assert (
mock_send.mock_calls[1].args[0]
== '<stream:features><starttls xmlns="urn:ietf:params:xml:ns:xmpp-tls"><required/></starttls><mechanisms xmlns="urn:ietf:params:xml:ns:xmpp-sasl"><mechanism>PLAIN</mechanism></mechanisms></stream:features>'
)
# Reset mock calls
mock_send.reset_mock()
mock_tls = xmppclient._handle_starttls = mock.Mock()
# Send start tls from "client"
test_data = "<starttls xmlns='urn:ietf:params:xml:ns:xmpp-tls'/>".encode("utf-8")
xmppclient._parse_data(test_data)
# After upgrading connection, server tells client to proceed with auth again
assert mock_tls.called
xmppclient.TLSUpgraded = True
# After TLS is upgraded, Client establishes session again and will auth this time
# Send connect stream from "client"
test_data = "<stream:stream xmlns='jabber:client' xmlns:stream='http://etherx.jabber.org/streams' version='1.0' to='ecouser.net'>".encode(
"utf-8"
)
xmppclient._parse_data(test_data)
# Expect 2 calls to send
assert mock_send.call_count == 2
# Server opens stream
assert (
mock_send.mock_calls[0].args[0]
== '<stream:stream xmlns:stream="http://etherx.jabber.org/streams" xmlns="jabber:client" version="1.0" id="1" from="ecouser.net">'
)
# Server tells client available features (without STARTTLS)
assert (
mock_send.mock_calls[1].args[0]
== '<stream:features><mechanisms xmlns="urn:ietf:params:xml:ns:xmpp-sasl"><mechanism>PLAIN</mechanism></mechanisms></stream:features>'
)
# Reset mock calls
mock_send.reset_mock()
# Client sends auth
test_data = '<auth xmlns="urn:ietf:params:xml:ns:xmpp-sasl" mechanism="PLAIN">AGZ1aWRfdG1wdXNlcgAwL0lPU0Y1M0QwN0JBL3VzXzg5ODgwMmZkYmM0NDQxYjBiYzgxNWIxZDFjNjgzMDJl</auth>'.encode(
"utf-8"
)
xmppclient._parse_data(test_data)
assert (
mock_send.mock_calls[0].args[0]
== '<success xmlns="urn:ietf:params:xml:ns:xmpp-sasl"/>'
) # Client successfully authenticated
assert xmppclient.state == xmppclient.INIT # Client moved to INIT state
async def test_xmpp_server_client_tls():
xmpp_address = ("127.0.0.1", 5223)
xmpp_server = bumper.XMPPServer(xmpp_address)
await xmpp_server.start_async_server()
with LogCapture("xmppserver") as l:
async def do_stuff_after_start_tls(
ssl_reader, ssl_writer
): # Used after starttls
writer.write(
b"<stream:stream xmlns='jabber:client' xmlns:stream='http://etherx.jabber.org/streams' version='1.0' to='ecouser.net'>"
) # New Stream
await writer.drain()
writer.write(
b'<auth xmlns="urn:ietf:params:xml:ns:xmpp-sasl" mechanism="PLAIN">AGZ1aWRfdG1wdXNlcgAwL0lPU0Y1M0QwN0JBL3VzXzg5ODgwMmZkYmM0NDQxYjBiYzgxNWIxZDFjNjgzMDJl</auth>'
) # Send Auth
await writer.drain()
reader, writer = await asyncio.open_connection("127.0.0.1", 5223)
writer.write(
b"<stream:stream xmlns='jabber:client' xmlns:stream='http://etherx.jabber.org/streams' version='1.0' to='ecouser.net'>"
) # Start stream
await writer.drain()
await asyncio.sleep(0.1)
writer.write(
b"<starttls xmlns='urn:ietf:params:xml:ns:xmpp-tls'/>"
) # Send StartTLS
await writer.drain()
await asyncio.sleep(0.1)
# Below will upgrade connection to TLS then callback to "do_stuff_after_start_tls"
ssl_context = ssl.create_default_context(ssl.Purpose.SERVER_AUTH)
ssl_context.check_hostname = False
ssl_context.load_verify_locations(cafile=bumper.ca_cert)
loop = asyncio.get_event_loop()
transport = writer.transport
protocol = writer.transport.get_protocol()
new_transport = await loop.start_tls(
transport, protocol, ssl_context, server_side=False
)
protocol._stream_reader = asyncio.StreamReader(loop=loop)
protocol._client_connected_cb = do_stuff_after_start_tls
protocol.connection_made(new_transport)
print(l)
async def test_client_init(*args, **kwargs):
test_transport = asyncio.Transport()
test_transport.get_extra_info = mock.Mock(return_value=mock_transport_extra_info())
test_transport.write = mock.Mock(return_value=return_send_data)
xmppclient = bumper.xmppserver.XMPPAsyncClient(test_transport)
xmppclient.state = xmppclient.INIT # Set client state to INIT
xmppclient.uid = "fuid_tmpuser"
xmppclient.resource = "IOSF53D07BA"
xmppclient.bumper_jid = "fuid_tmpuser@ecouser.net/IOSF53D07BA"
xmppclient.type = xmppclient.CONTROLLER
mock_send = xmppclient.send = mock.Mock(side_effect=return_send_data)
# Send connect stream from "client"
test_data = "<stream:stream xmlns='jabber:client' xmlns:stream='http://etherx.jabber.org/streams' version='1.0' to='ecouser.net'>".encode(
"utf-8"
)
xmppclient._parse_data(test_data)
# Expect 2 calls to send
assert mock_send.call_count == 2
# Server opens stream
assert (
mock_send.mock_calls[0].args[0]
== '<stream:stream xmlns:stream="http://etherx.jabber.org/streams" xmlns="jabber:client" version="1.0" id="1" from="ecouser.net">'
)
# Server tells client binds
assert (
mock_send.mock_calls[1].args[0]
== '<stream:features><bind xmlns="urn:ietf:params:xml:ns:xmpp-bind"/><session xmlns="urn:ietf:params:xml:ns:xmpp-session"/></stream:features>'
)
# Reset mock calls
mock_send.reset_mock()
# Send bind from "client"
test_data = '<iq type="set" id="5E9872D5-547E-49AF-AE51-9EFAA282F952"><bind xmlns="urn:ietf:params:xml:ns:xmpp-bind"><resource>IOSF53D07BA</resource></bind></iq>'.encode(
"utf-8"
)
xmppclient._parse_data(test_data)
assert (
mock_send.mock_calls[0].args[0]
== '<iq type="result" id="5E9872D5-547E-49AF-AE51-9EFAA282F952"><bind xmlns="urn:ietf:params:xml:ns:xmpp-bind"><jid>fuid_tmpuser@ecouser.net/IOSF53D07BA</jid></bind></iq>'
) # client successfully binded
assert xmppclient.state == xmppclient.BIND # client moved to BIND state
# Reset mock calls
mock_send.reset_mock()
# Send set session from client
test_data = '<iq type="set" id="FA1041E7-AA27-43DD-BAA3-64DE2DE56AA3"><session xmlns="urn:ietf:params:xml:ns:xmpp-session"/></iq>'.encode(
"utf-8"
)
xmppclient._parse_data(test_data)
assert xmppclient.state == xmppclient.READY # client moved to READY state
assert (
mock_send.mock_calls[0].args[0]
== '<iq type="result" id="FA1041E7-AA27-43DD-BAA3-64DE2DE56AA3" />'
) # client ready
# Reset mock calls
mock_send.reset_mock()
# Send presense from client
test_data = '<presence type="available"/>'.encode("utf-8")
xmppclient._parse_data(test_data)
assert (
mock_send.mock_calls[0].args[0]
== '<presence to="fuid_tmpuser@ecouser.net/IOSF53D07BA"> dummy </presence>'
) # client presence - dummy response
async def test_bot_connect(*args, **kwargs):
test_transport = asyncio.Transport()
test_transport.get_extra_info = mock.Mock(return_value=mock_transport_extra_info())
test_transport.write = mock.Mock(return_value=return_send_data)
xmppclient = bumper.xmppserver.XMPPAsyncClient(test_transport)
xmppclient.state = xmppclient.CONNECT # Set client state to CONNECT
mock_send = xmppclient.send = mock.Mock(side_effect=return_send_data)
# Send connect stream from "bot"
test_data = "<stream:stream xmlns:stream='http://etherx.jabber.org/streams' xmlns='jabber:client' to='159.ecorobot.net' version='1.0'>".encode(
"utf-8"
)
xmppclient._parse_data(test_data)
# Expect 2 calls to send
assert mock_send.call_count == 2
# Server opens stream
assert (
mock_send.mock_calls[0].args[0]
== '<stream:stream xmlns:stream="http://etherx.jabber.org/streams" xmlns="jabber:client" version="1.0" id="1" from="ecouser.net">'
)
# Server tells client available features
assert (
mock_send.mock_calls[1].args[0]
== '<stream:features><starttls xmlns="urn:ietf:params:xml:ns:xmpp-tls"><required/></starttls><mechanisms xmlns="urn:ietf:params:xml:ns:xmpp-sasl"><mechanism>PLAIN</mechanism></mechanisms></stream:features>'
)
# Reset mock calls
mock_send.reset_mock()
# Send auth from "bot"
test_data = "<auth xmlns='urn:ietf:params:xml:ns:xmpp-sasl' mechanism='PLAIN'>AEUwMDAwMDAwMDAwMDAwMDAxMjM0AGVuY3J5cHRlZF9wYXNz</auth>".encode(
"utf-8"
)
xmppclient._parse_data(test_data)
assert (
mock_send.mock_calls[0].args[0]
== '<success xmlns="urn:ietf:params:xml:ns:xmpp-sasl"/>'
) # Bot successfully authenticated
assert xmppclient.state == xmppclient.INIT # Bot moved to INIT state
assert xmppclient.type == xmppclient.BOT # Client type is now bot
async def test_bot_init(*args, **kwargs):
test_transport = asyncio.Transport()
test_transport.get_extra_info = mock.Mock(return_value=mock_transport_extra_info())
test_transport.write = mock.Mock(return_value=return_send_data)
xmppclient = bumper.xmppserver.XMPPAsyncClient(test_transport)
xmppclient.state = xmppclient.INIT # Set client state to INIT
xmppclient.uid = "E0000000000000001234"
xmppclient.devclass = "159"
xmppclient.type = xmppclient.BOT
mock_send = xmppclient.send = mock.Mock(side_effect=return_send_data)
# Send connect stream from "bot"
test_data = "<stream:stream xmlns:stream='http://etherx.jabber.org/streams' xmlns='jabber:client' to='159.ecorobot.net' version='1.0'>".encode(
"utf-8"
)
xmppclient._parse_data(test_data)
# Expect 2 calls to send
assert mock_send.call_count == 2
# Server opens stream
assert (
mock_send.mock_calls[0].args[0]
== '<stream:stream xmlns:stream="http://etherx.jabber.org/streams" xmlns="jabber:client" version="1.0" id="1" from="ecouser.net">'
)
# Server tells client binds
assert (
mock_send.mock_calls[1].args[0]
== '<stream:features><bind xmlns="urn:ietf:params:xml:ns:xmpp-bind"/><session xmlns="urn:ietf:params:xml:ns:xmpp-session"/></stream:features>'
)
# Reset mock calls
mock_send.reset_mock()
# Send bind from "bot"
test_data = "<iq type='set' id='2521'><bind xmlns='urn:ietf:params:xml:ns:xmpp-bind'><resource>atom</resource></bind></iq>".encode(
"utf-8"
)
xmppclient._parse_data(test_data)
assert (
mock_send.mock_calls[0].args[0]
== '<iq type="result" id="2521"><bind xmlns="urn:ietf:params:xml:ns:xmpp-bind"><jid>E0000000000000001234@159.ecorobot.net/atom</jid></bind></iq>'
) # Bot successfully binded
assert xmppclient.state == xmppclient.BIND # Bot moved to BIND state
# Reset mock calls
mock_send.reset_mock()
# Send set session from bot
test_data = "<iq type='set' id='2522'><session xmlns='urn:ietf:params:xml:ns:xmpp-session'/></iq>".encode(
"utf-8"
)
xmppclient._parse_data(test_data)
assert xmppclient.state == xmppclient.READY # Bot moved to READY state
assert (
mock_send.mock_calls[0].args[0] == '<iq type="result" id="2522" />'
) # Bot ready
# Reset mock calls
mock_send.reset_mock()
# Send presense from bot
test_data = "<presence><status>hello world</status></presence><iq type='result' from='E0000000000000001234@159.ecorobot.net/atom' to='ecouser.net' id='s2c1'/>".encode(
"utf-8"
)
xmppclient._parse_data(test_data)
assert (
mock_send.mock_calls[0].args[0]
== '<presence to="E0000000000000001234@159.ecorobot.net/atom"> dummy </presence>'
) # bot presence - dummy response
async def test_ping_server(*args, **kwargs):
test_transport = asyncio.Transport()
test_transport.get_extra_info = mock.Mock(return_value=mock_transport_extra_info())
test_transport.write = mock.Mock(return_value=return_send_data)
xmppclient = bumper.xmppserver.XMPPAsyncClient(test_transport)
xmppclient.state = xmppclient.READY # Set client state to READY
xmppclient.uid = "E0000000000000001234"
xmppclient.devclass = "159"
mock_send = xmppclient.send = mock.Mock(side_effect=return_send_data)
# Ping from bot
test_data = '<iq xmlns:ns0="urn:xmpp:ping" from="E000BVTNX18700260382@159.ecorobot.net/atom" id="2542" to="159.ecorobot.net" type="get"><ping /></iq>'.encode(
"utf-8"
)
xmppclient._parse_data(test_data)
assert (
mock_send.mock_calls[0].args[0]
== '<iq type="result" id="2542" from="159.ecorobot.net" />'
) # ping response
async def test_ping_client_to_client(*args, **kwargs):
test_transport = asyncio.Transport()
test_transport.get_extra_info = mock.Mock(return_value=mock_transport_extra_info())
test_transport.write = mock.Mock(return_value=return_send_data)
xmppclient = bumper.xmppserver.XMPPAsyncClient(test_transport)
xmppclient.state = xmppclient.READY # Set client state to READY
xmppclient.uid = "E0000000000000001234"
xmppclient.devclass = "159"
xmppclient.bumper_jid = "E0000000000000001234@159.ecorobot.net/atom"
mock_send = xmppclient.send = mock.Mock(side_effect=return_send_data)
xmppclient2 = bumper.xmppserver.XMPPAsyncClient(test_transport)
xmppclient2.state = xmppclient.READY # Set client state to READY
xmppclient2.uid = "fuid_tmpuser"
xmppclient2.resource = "IOSF53D07BA"
xmppclient2.bumper_jid = "fuid_tmpuser@ecouser.net/IOSF53D07BA"
mock_send2 = xmppclient2.send = mock.Mock(side_effect=return_send_data)
bumper.xmppserver.XMPPServer.clients.append(xmppclient)
bumper.xmppserver.XMPPServer.clients.append(xmppclient2)
# Ping from user to bot
test_data = '<iq id="104934615" to="fuid_tmpuser@ecouser.net/IOSF53D07BA" type="get"><ping xmlns="urn:xmpp:ping" /></iq>'.encode(
"utf-8"
)
xmppclient._parse_data(test_data)
assert (
mock_send2.mock_calls[0].args[0]
== '<iq from="E0000000000000001234@159.ecorobot.net/atom" id="104934615" to="fuid_tmpuser@ecouser.net/IOSF53D07BA" type="get"><ping xmlns="urn:xmpp:ping" /></iq>'
) # ping response
# Ping response from bot to user
test_data = "<iq type='result' to='E0000000000000001234@159.ecorobot.net/atom' id='104934615'/>".encode(
"utf-8"
)
xmppclient2._parse_data(test_data)
assert (
mock_send.mock_calls[0].args[0]
== '<iq from="fuid_tmpuser@ecouser.net/IOSF53D07BA" id="104934615" to="E0000000000000001234@159.ecorobot.net/atom" type="result" />'
) # ping response
async def test_client_send_iq(*args, **kwargs):
test_transport = asyncio.Transport()
test_transport.get_extra_info = mock.Mock(return_value=mock_transport_extra_info())
test_transport.write = mock.Mock(return_value=return_send_data)
xmppclient = bumper.xmppserver.XMPPAsyncClient(test_transport)
xmppclient.state = xmppclient.READY # Set client state to READY
xmppclient.uid = "fuid_tmpuser"
xmppclient.resource = "IOSF53D07BA"
xmppclient.bumper_jid = "fuid_tmpuser@ecouser.net/IOSF53D07BA"
xmppclient.type - xmppclient.CONTROLLER
mock_send = xmppclient.send = mock.Mock(side_effect=return_send_data)
bumper.xmppserver.XMPPServer.clients.append(xmppclient)
xmppclient2 = bumper.xmppserver.XMPPAsyncClient(test_transport)
xmppclient2.state = xmppclient.READY # Set client state to READY
xmppclient2.uid = "E0000000000000001234"
xmppclient2.devclass = "159"
xmppclient2.bumper_jid = "E0000000000000001234@159.ecorobot.net/atom"
xmppclient2.type = xmppclient2.BOT
mock_send2 = xmppclient2.send = mock.Mock(side_effect=return_send_data)
bumper.xmppserver.XMPPServer.clients.append(xmppclient2)
# Roster IQ - Only seen from Android app so far
test_data = '<iq id="EE0XQ-2" type="get"><query xmlns="jabber:iq:roster" ></query></iq>'.encode(
"utf-8"
)
xmppclient._parse_data(test_data)
assert (
mock_send.mock_calls[0].args[0]
== '<iq type="error" id="EE0XQ-2"><error type="cancel" code="501"><feature-not-implemented xmlns="urn:ietf:params:xml:ns:xmpp-stanzas"/></error></iq>'
) # feature not implemented response
# Reset mock calls
mock_send.reset_mock()
# Bot Command
test_data = '<iq id="7" to="E0000000000000001234@159.ecorobot.net/atom" type="set"><query xmlns="com:ctl"><ctl id="72107787" td="GetCleanState" /></query></iq>'.encode(
"utf-8"
)
xmppclient._parse_data(test_data)
assert (
mock_send2.mock_calls[0].args[0]
== '<iq from="fuid_tmpuser@ecouser.net/IOSF53D07BA" id="7" to="E0000000000000001234@159.ecorobot.net/atom" type="set"><query xmlns="com:ctl"><ctl id="72107787" td="GetCleanState" /></query></iq>'
) # command was sent to bot
# Reset mock calls
mock_send.reset_mock()
# Bot response to query
test_data = '<iq xmlns:ns0="com:ctl" id="2679" to="fuid_tmpuser@ecouser.net/IOSF53D07BA" type="set"><query><ctl td="ChargeState"><charge h="0" r="a" type="Going" /></ctl></query></iq>'.encode(
"utf-8"
)
xmppclient2._parse_data(test_data)
assert (
mock_send.mock_calls[0].args[0]
== '<iq from="E0000000000000001234@159.ecorobot.net/atom" id="2679" to="fuid_tmpuser@ecouser.net/IOSF53D07BA" type="set"><query xmlns="com:ctl"><ctl td="ChargeState"><charge h="0" r="a" type="Going" /></ctl></query></iq>'
) # result sent to client
# Reset mock calls
mock_send.reset_mock()
# Bot result
test_data = "<iq type='result' from='E0000000000000001234@159.ecorobot.net/atom' to='ecouser.net' id='s2c1'/>".encode(
"utf-8"
)
xmppclient2._parse_data(test_data)
assert (
mock_send.mock_calls[0].args[0]
== '<iq from="E0000000000000001234@159.ecorobot.net/atom" id="s2c1" to="ecouser.net" type="result" />'
) # result sent to ecouser.net
# Reset mock calls
mock_send.reset_mock()
# Bot iq set
test_data = "<iq to='fuid_tmpuser@ecouser.net/IOSF53D07BA' type='set' id='2700'><query xmlns='com:ctl'><ctl td='BatteryInfo'><battery power='100'/></ctl></query></iq>".encode(
"utf-8"
)
xmppclient2._parse_data(test_data)
assert (
mock_send.mock_calls[0].args[0]
== '<iq from="E0000000000000001234@159.ecorobot.net/atom" id="2700" to="fuid_tmpuser@ecouser.net/IOSF53D07BA" type="set"><query xmlns="com:ctl"><ctl td="BatteryInfo"><battery power="100" /></ctl></query></iq>'
) # result sent to ecouser.net
# Reset mock calls
mock_send.reset_mock()
# Bot error report
test_data = "<iq to='fuid_tmpuser@ecouser.net/IOSF53D07BA' type='set' id='631'><query xmlns='com:ctl'><ctl td='error' errs='102'/></query></iq>".encode(
"utf-8"
)
xmppclient2._parse_data(test_data)
assert (
mock_send.mock_calls[0].args[0]
== '<iq from="E0000000000000001234@159.ecorobot.net/atom" id="631" to="fuid_tmpuser@ecouser.net/IOSF53D07BA" type="set"><query xmlns="com:ctl"><ctl errs="102" td="error" /></query></iq>'
) # result sent to ecouser.net
# Reset mock calls
mock_send.reset_mock()
# Bot "DorpError" to all
test_data = "<iq to='rl.ecorobot.net' type='set' id='1234'><query xmlns='com:sf'><sf td='pub' t='log' ts='1559893796000' tp='p' k='DeviceAlert' v='DorpError' f='E0000000000000001234@159.ecorobot.net' g='fuid_tmpuser@ecouser.net'/></query></iq>".encode(
"utf-8"
)
xmppclient2._parse_data(test_data)
assert (
mock_send.mock_calls[0].args[0]
== '<iq xmlns="com:sf" from="E0000000000000001234@159.ecorobot.net/atom" id="1234" to="rl.ecorobot.net" type="set"><query xmlns="com:ctl"><sf f="E0000000000000001234@159.ecorobot.net" g="fuid_tmpuser@ecouser.net" k="DeviceAlert" t="log" td="pub" tp="p" ts="1559893796000" v="DorpError" /></query></iq>'
) # result sent to ecouser.net
# Reset mock calls
mock_send.reset_mock()