Remove creepy xmpp thingy
This commit is contained in:
parent
8122919f77
commit
247e1a0977
15 changed files with 7 additions and 1983 deletions
|
|
@ -2,7 +2,6 @@
|
|||
|
||||
from bumper.confserver import ConfServer
|
||||
from bumper.mqttserver import MQTTServer, MQTTHelperBot
|
||||
from bumper.xmppserver import XMPPServer
|
||||
from bumper.models import *
|
||||
from bumper.db import *
|
||||
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")
|
||||
|
||||
# Listeners
|
||||
bumper_listen = os.environ.get("BUMPER_LISTEN") or socket.gethostbyname(
|
||||
socket.gethostname()
|
||||
)
|
||||
bumper_listen = os.environ.get("BUMPER_LISTEN") or "192.168.1.163"
|
||||
|
||||
|
||||
bumper_announce_ip = os.environ.get("BUMPER_ANNOUNCE_IP") or bumper_listen
|
||||
|
|
@ -60,7 +57,6 @@ mqtt_server = None
|
|||
mqtt_helperbot = None
|
||||
conf_server = None
|
||||
conf_server_2 = None
|
||||
xmpp_server = None
|
||||
|
||||
# Plugins
|
||||
sys.path.append(os.path.join(bumper_dir, "bumper", "plugins"))
|
||||
|
|
@ -141,22 +137,12 @@ boterrorlog.addHandler(boterrorlog_rotate)
|
|||
# Override the logging level
|
||||
# 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
|
||||
|
||||
|
||||
mqtt_listen_port = 8883
|
||||
conf1_listen_port = 443
|
||||
conf2_listen_port = 8007
|
||||
xmpp_listen_port = 5223
|
||||
|
||||
|
||||
async def start():
|
||||
|
|
@ -200,8 +186,6 @@ async def start():
|
|||
conf_server = ConfServer((bumper_listen, conf1_listen_port), usessl=True)
|
||||
global conf_server_2
|
||||
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
|
||||
asyncio.create_task(mqtt_server.broker_coro())
|
||||
|
|
@ -209,9 +193,6 @@ async def start():
|
|||
# Start MQTT Helperbot
|
||||
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
|
||||
while mqtt_helperbot.Client is None:
|
||||
await asyncio.sleep(0.1)
|
||||
|
|
@ -248,10 +229,6 @@ async def shutdown():
|
|||
if mqtt_server.broker.transitions.state == "started":
|
||||
await mqtt_server.broker.shutdown()
|
||||
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
|
||||
shutting_down = True
|
||||
|
||||
|
|
|
|||
|
|
@ -118,8 +118,8 @@ class ConfServer:
|
|||
ssl_ctx.load_cert_chain(bumper.server_cert, bumper.server_key)
|
||||
site = web.TCPSite(
|
||||
runner,
|
||||
host=address,
|
||||
port=port,
|
||||
host="0.0.0.0",
|
||||
port="443",
|
||||
ssl_context=ssl_ctx,
|
||||
)
|
||||
|
||||
|
|
@ -180,7 +180,6 @@ class ConfServer:
|
|||
clients = bumper.db_get().table("clients").all()
|
||||
helperbot = bumper.mqtt_helperbot.Client.session.transitions.state
|
||||
mqttserver = bumper.mqtt_server.broker
|
||||
xmppserver = bumper.xmpp_server
|
||||
mq_sessions = []
|
||||
for sess in mqttserver._sessions:
|
||||
tmpsess = []
|
||||
|
|
@ -203,8 +202,7 @@ class ConfServer:
|
|||
{"clients": mq_sessions},
|
||||
]
|
||||
},
|
||||
],
|
||||
"xmpp_server": xmppserver
|
||||
]
|
||||
}
|
||||
resp = aiohttp_jinja2.render_template('home.jinja2', request, context=all)
|
||||
#return web.json_response(all)
|
||||
|
|
@ -335,10 +333,6 @@ class ConfServer:
|
|||
1.5, lambda: asyncio.create_task(bumper.mqtt_server.broker_coro())
|
||||
) # 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):
|
||||
try:
|
||||
service = request.match_info.get("service", "")
|
||||
|
|
@ -352,9 +346,6 @@ class ConfServer:
|
|||
5, lambda: asyncio.create_task(self.restart_Helper())
|
||||
) # In 5 seconds restart Helperbot
|
||||
|
||||
return web.json_response({"status": "complete"})
|
||||
elif service == "XMPPServer":
|
||||
await self.restart_XMPP()
|
||||
return web.json_response({"status": "complete"})
|
||||
else:
|
||||
return web.json_response({"status": "invalid service"})
|
||||
|
|
|
|||
17
bumper/db.py
17
bumper/db.py
|
|
@ -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):
|
||||
bumperlog.debug("Checking for authcode: {}".format(authcode))
|
||||
tokens = db_get().table("tokens")
|
||||
|
|
@ -328,12 +322,6 @@ def bot_set_mqtt(did, mqtt):
|
|||
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):
|
||||
newclient = VacBotClient()
|
||||
newclient.userid = userid
|
||||
|
|
@ -368,8 +356,3 @@ def client_set_mqtt(resource, mqtt):
|
|||
Client = Query()
|
||||
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)
|
||||
|
|
|
|||
|
|
@ -13,7 +13,6 @@ class VacBotDevice(object):
|
|||
self.nick = nick
|
||||
self.resource = resource
|
||||
self.mqtt_connection = False
|
||||
self.xmpp_connection = False
|
||||
|
||||
def asdict(self):
|
||||
return {
|
||||
|
|
@ -24,7 +23,6 @@ class VacBotDevice(object):
|
|||
"nick": self.nick,
|
||||
"resource": self.resource,
|
||||
"mqtt_connection": self.mqtt_connection,
|
||||
"xmpp_connection": self.xmpp_connection,
|
||||
}
|
||||
|
||||
|
||||
|
|
@ -52,7 +50,6 @@ class VacBotClient(object):
|
|||
self.realm = realm
|
||||
self.resource = token
|
||||
self.mqtt_connection = False
|
||||
self.xmpp_connection = False
|
||||
|
||||
def asdict(self):
|
||||
return {
|
||||
|
|
@ -60,7 +57,6 @@ class VacBotClient(object):
|
|||
"realm": self.realm,
|
||||
"resource": self.resource,
|
||||
"mqtt_connection": self.mqtt_connection,
|
||||
"xmpp_connection": self.xmpp_connection,
|
||||
}
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -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&ls=1&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'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()
|
||||
|
||||
|
|
@ -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()
|
||||
|
|
@ -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()
|
||||
|
||||
|
|
@ -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()
|
||||
|
||||
|
|
@ -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()
|
||||
|
||||
|
|
@ -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()
|
||||
|
||||
|
|
@ -92,33 +92,6 @@
|
|||
|
||||
</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 card-header">
|
||||
<h3 class="card-title">Helperbot</h3>
|
||||
|
|
@ -145,7 +118,7 @@
|
|||
<div class="card-body">
|
||||
<table class="table table-striped table-bordered table-responsive-lg">
|
||||
<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>
|
||||
{% for bot in bots %}
|
||||
<TR>
|
||||
|
|
@ -157,7 +130,6 @@
|
|||
<TD>{{ bot.company}} </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>
|
||||
</TR>
|
||||
{% endfor %}
|
||||
|
|
@ -175,7 +147,7 @@
|
|||
<div class="card-body">
|
||||
<table class="table table-striped table-bordered table-responsive-lg">
|
||||
<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>
|
||||
{% for client in clients %}
|
||||
<TR>
|
||||
|
|
@ -183,7 +155,6 @@
|
|||
<TD>{{ client.realm }}</TD>
|
||||
<TD>{{ client.resource }}</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>
|
||||
</TR>
|
||||
{% endfor %}
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
@ -94,12 +94,6 @@ async def test_base(aiohttp_client):
|
|||
bumper.mqtt_server = mqtt_server
|
||||
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
|
||||
mqtt_helperbot = bumper.MQTTHelperBot(mqtt_address)
|
||||
bumper.mqtt_helperbot = mqtt_helperbot
|
||||
|
|
@ -113,8 +107,6 @@ async def test_base(aiohttp_client):
|
|||
|
||||
await mqtt_server.broker.shutdown()
|
||||
|
||||
bumper.xmpp_server.disconnect()
|
||||
|
||||
|
||||
async def test_restartService(aiohttp_client):
|
||||
remove_existing_db()
|
||||
|
|
@ -126,12 +118,6 @@ async def test_restartService(aiohttp_client):
|
|||
bumper.mqtt_server = mqtt_server
|
||||
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
|
||||
mqtt_helperbot = bumper.MQTTHelperBot(mqtt_address)
|
||||
bumper.mqtt_helperbot = mqtt_helperbot
|
||||
|
|
@ -145,14 +131,9 @@ async def test_restartService(aiohttp_client):
|
|||
resp = await client.get("/restart_MQTTServer")
|
||||
assert resp.status == 200
|
||||
|
||||
resp = await client.get("/restart_XMPPServer")
|
||||
assert resp.status == 200
|
||||
|
||||
mqtt_helperbot.Client.disconnect()
|
||||
await mqtt_server.broker.shutdown()
|
||||
|
||||
xmpp_server.disconnect()
|
||||
|
||||
async def test_RemoveBot(aiohttp_client):
|
||||
client = await aiohttp_client(create_app)
|
||||
resp = await client.get("/bot/remove/test_did")
|
||||
|
|
|
|||
|
|
@ -117,11 +117,6 @@ def test_bot_db():
|
|||
"mqtt_connection"
|
||||
] # 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")
|
||||
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"
|
||||
] # 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")
|
||||
assert bumper.client_get("resource_123") == None
|
||||
|
|
|
|||
|
|
@ -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()
|
||||
|
||||
Loading…
Add table
Add a link
Reference in a new issue