Add XEP-0100 (Gateway Interaction) plugin
Remove usused prompt_future attribute Add plugin_end Update with mathieui's comments Add option to transfer messages from unregistered users XEP 0100 plugin
This commit is contained in:
parent
2dac77e680
commit
9b5f3d9df0
9
docs/api/plugins/xep_0100.rst
Normal file
9
docs/api/plugins/xep_0100.rst
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
|
||||||
|
XEP-0106: Gateway interaction
|
||||||
|
=============================
|
||||||
|
|
||||||
|
.. module:: slixmpp.plugins.xep_0100
|
||||||
|
|
||||||
|
.. autoclass:: XEP_0100
|
||||||
|
:members:
|
||||||
|
:exclude-members: session_bind, plugin_init, plugin_end
|
@ -42,6 +42,7 @@ __all__ = [
|
|||||||
'xep_0092', # Software Version
|
'xep_0092', # Software Version
|
||||||
# 'xep_0095', # Legacy Stream Initiation. Don’t automatically load
|
# 'xep_0095', # Legacy Stream Initiation. Don’t automatically load
|
||||||
# 'xep_0096', # Legacy SI File Transfer. Don’t automatically load
|
# 'xep_0096', # Legacy SI File Transfer. Don’t automatically load
|
||||||
|
'xep_0100', # Gateway interaction
|
||||||
'xep_0106', # JID Escaping
|
'xep_0106', # JID Escaping
|
||||||
'xep_0107', # User Mood
|
'xep_0107', # User Mood
|
||||||
'xep_0108', # User Activity
|
'xep_0108', # User Activity
|
||||||
|
6
slixmpp/plugins/xep_0100/__init__.py
Normal file
6
slixmpp/plugins/xep_0100/__init__.py
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
from slixmpp.plugins.base import register_plugin
|
||||||
|
|
||||||
|
from slixmpp.plugins.xep_0100.gateway import XEP_0100, LegacyError
|
||||||
|
|
||||||
|
|
||||||
|
register_plugin(XEP_0100)
|
257
slixmpp/plugins/xep_0100/gateway.py
Normal file
257
slixmpp/plugins/xep_0100/gateway.py
Normal file
@ -0,0 +1,257 @@
|
|||||||
|
import asyncio
|
||||||
|
import logging
|
||||||
|
from functools import partial
|
||||||
|
import typing
|
||||||
|
|
||||||
|
from slixmpp import Message, Iq, Presence, JID
|
||||||
|
from slixmpp.xmlstream.handler import Callback
|
||||||
|
from slixmpp.xmlstream.matcher import StanzaPath
|
||||||
|
from slixmpp.plugins import BasePlugin
|
||||||
|
|
||||||
|
|
||||||
|
class XEP_0100(BasePlugin):
|
||||||
|
|
||||||
|
"""
|
||||||
|
XEP-0100: Gateway interaction
|
||||||
|
|
||||||
|
Does not cover the deprecated Agent Information and 'jabber:iq:gateway' protocols
|
||||||
|
|
||||||
|
Events registered by this plugin:
|
||||||
|
|
||||||
|
- legacy_login: Jabber user got online or just registered
|
||||||
|
- legacy_logout: Jabber user got offline or just unregistered
|
||||||
|
- legacy_presence_unavailable: Jabber user sent an unavailable presence to a legacy contact
|
||||||
|
- gateway_message: Jabber user sent a direct message to the gateway component
|
||||||
|
- legacy_message: Jabber user sent a message to the legacy network
|
||||||
|
|
||||||
|
|
||||||
|
Plugin Parameters:
|
||||||
|
|
||||||
|
- `component_name`: (str) Name of the entity
|
||||||
|
- `type`: (str) Type of the gateway identity. Should be the name of the legacy service
|
||||||
|
- `needs_registration`: (bool) If set to True, messages received from unregistered users will
|
||||||
|
not be transmitted to the legacy service
|
||||||
|
|
||||||
|
API:
|
||||||
|
|
||||||
|
- legacy_contact_add(jid, node, ifrom: JID, args: JID): Add contact on the legacy service.
|
||||||
|
Should raise LegacyError if anything goes wrong in the process.
|
||||||
|
`ifrom` is the gateway user's JID and `args` is the legacy contact's JID.
|
||||||
|
- legacy_contact_remove(jid, node, ifrom: JID, args: JID): Remove a contact.
|
||||||
|
|
||||||
|
"""
|
||||||
|
|
||||||
|
name = "xep_0100"
|
||||||
|
description = "XEP-0100: Gateway interaction"
|
||||||
|
dependencies = {
|
||||||
|
"xep_0030", # Service discovery
|
||||||
|
"xep_0077", # In band registration
|
||||||
|
}
|
||||||
|
|
||||||
|
default_config = {
|
||||||
|
"component_name": "SliXMPP gateway",
|
||||||
|
"type": "xmpp",
|
||||||
|
"needs_registration": True,
|
||||||
|
}
|
||||||
|
|
||||||
|
def plugin_init(self):
|
||||||
|
if not self.xmpp.is_component:
|
||||||
|
log.error("Only components can be gateways, aborting plugin load")
|
||||||
|
return
|
||||||
|
|
||||||
|
self.xmpp["xep_0030"].add_identity(
|
||||||
|
name=self.component_name, category="gateway", itype=self.type
|
||||||
|
)
|
||||||
|
|
||||||
|
self.api.register(self._legacy_contact_remove, "legacy_contact_remove")
|
||||||
|
self.api.register(self._legacy_contact_add, "legacy_contact_add")
|
||||||
|
|
||||||
|
# Without that BaseXMPP sends unsub/unavailable on sub requests and we don't want that
|
||||||
|
self.xmpp.client_roster.auto_authorize = True
|
||||||
|
self.xmpp.client_roster.auto_subscribe = False
|
||||||
|
|
||||||
|
self.xmpp.add_event_handler("user_register", self.on_user_register)
|
||||||
|
self.xmpp.add_event_handler("user_unregister", self.on_user_unregister)
|
||||||
|
self.xmpp.add_event_handler("presence_available", self.on_presence_available)
|
||||||
|
self.xmpp.add_event_handler(
|
||||||
|
"presence_unavailable", self.on_presence_unavailable
|
||||||
|
)
|
||||||
|
self.xmpp.add_event_handler("presence_subscribe", self.on_presence_subscribe)
|
||||||
|
self.xmpp.add_event_handler(
|
||||||
|
"presence_unsubscribe", self.on_presence_unsubscribe
|
||||||
|
)
|
||||||
|
self.xmpp.add_event_handler("message", self.on_message)
|
||||||
|
|
||||||
|
def plugin_end(self):
|
||||||
|
if not self.xmpp.is_component:
|
||||||
|
return
|
||||||
|
|
||||||
|
self.xmpp.del_event_handler("user_register", self.on_user_register)
|
||||||
|
self.xmpp.del_event_handler("user_unregister", self.on_user_unregister)
|
||||||
|
self.xmpp.del_event_handler("presence_available", self.on_presence_available)
|
||||||
|
self.xmpp.del_event_handler(
|
||||||
|
"presence_unavailable", self.on_presence_unavailable
|
||||||
|
)
|
||||||
|
self.xmpp.del_event_handler("presence_subscribe", self.on_presence_subscribe)
|
||||||
|
self.xmpp.del_event_handler("message", self.on_message)
|
||||||
|
self.xmpp.del_event_handler(
|
||||||
|
"presence_unsubscribe", self.on_presence_unsubscribe
|
||||||
|
)
|
||||||
|
|
||||||
|
async def get_user(self, stanza):
|
||||||
|
return await self.xmpp["xep_0077"].api["user_get"](None, None, None, stanza)
|
||||||
|
|
||||||
|
def send_presence(self, pto, ptype=None, pstatus=None, pfrom=None):
|
||||||
|
self.xmpp.send_presence(
|
||||||
|
pfrom=self.xmpp.boundjid.bare,
|
||||||
|
ptype=ptype,
|
||||||
|
pto=pto,
|
||||||
|
pstatus=pstatus,
|
||||||
|
)
|
||||||
|
|
||||||
|
async def on_user_register(self, iq: Iq):
|
||||||
|
user_jid = iq["from"]
|
||||||
|
user = await self.get_user(iq)
|
||||||
|
if user is None: # This should not happen
|
||||||
|
log.warning(f"{user_jid} has registered but cannot find them in user store")
|
||||||
|
else:
|
||||||
|
log.debug(f"Sending subscription request to {user_jid}")
|
||||||
|
self.xmpp.client_roster.subscribe(user_jid)
|
||||||
|
|
||||||
|
def on_user_unregister(self, iq: Iq):
|
||||||
|
user_jid = iq["from"]
|
||||||
|
log.debug(f"Sending subscription request to {user_jid}")
|
||||||
|
self.xmpp.event("legacy_logout", iq)
|
||||||
|
self.xmpp.client_roster.unsubscribe(iq["from"])
|
||||||
|
self.xmpp.client_roster.remove(iq["from"])
|
||||||
|
log.debug(f"roster: {self.xmpp.client_roster}")
|
||||||
|
|
||||||
|
async def on_presence_available(self, presence: Presence):
|
||||||
|
user_jid = presence["from"]
|
||||||
|
user = await self.get_user(presence)
|
||||||
|
if user is None:
|
||||||
|
log.warning(
|
||||||
|
f"{user_jid} has gotten online but cannot find them in user store"
|
||||||
|
)
|
||||||
|
else:
|
||||||
|
self.xmpp.event("legacy_login", presence)
|
||||||
|
log.debug(f"roster: {self.xmpp.client_roster}")
|
||||||
|
self.send_presence(pto=user_jid.bare, ptype="available")
|
||||||
|
|
||||||
|
async def on_presence_unavailable(self, presence: Presence):
|
||||||
|
user_jid = presence["from"]
|
||||||
|
user = await self.get_user(presence)
|
||||||
|
if user is None: # This should not happen
|
||||||
|
log.warning(
|
||||||
|
f"{user_jid} has gotten offline but but cannot find them in user store"
|
||||||
|
)
|
||||||
|
return
|
||||||
|
|
||||||
|
if presence["to"] == self.xmpp.boundjid.bare:
|
||||||
|
self.xmpp.event("legacy_logout", presence)
|
||||||
|
self.send_presence(pto=user_jid, ptype="unavailable")
|
||||||
|
else:
|
||||||
|
self.xmpp.event("legacy_presence_unavailable", presence)
|
||||||
|
|
||||||
|
async def _legacy_contact_add(self, jid, node, ifrom, contact_jid: JID):
|
||||||
|
pass
|
||||||
|
|
||||||
|
async def on_presence_subscribe(self, presence: Presence):
|
||||||
|
user_jid = presence["from"]
|
||||||
|
user = await self.get_user(presence)
|
||||||
|
if user is None and self.needs_registration:
|
||||||
|
return
|
||||||
|
|
||||||
|
if presence["to"] == self.xmpp.boundjid.bare:
|
||||||
|
return
|
||||||
|
|
||||||
|
try:
|
||||||
|
await self.api["legacy_contact_add"](
|
||||||
|
ifrom=user_jid,
|
||||||
|
args=presence["to"],
|
||||||
|
)
|
||||||
|
except LegacyError:
|
||||||
|
self.xmpp.send_presence(
|
||||||
|
pfrom=presence["to"],
|
||||||
|
ptype="unsubscribed",
|
||||||
|
pto=user_jid,
|
||||||
|
)
|
||||||
|
return
|
||||||
|
self.xmpp.send_presence(
|
||||||
|
pfrom=presence["to"],
|
||||||
|
ptype="subscribed",
|
||||||
|
pto=user_jid,
|
||||||
|
)
|
||||||
|
self.xmpp.send_presence(
|
||||||
|
pfrom=presence["to"],
|
||||||
|
pto=user_jid,
|
||||||
|
)
|
||||||
|
self.xmpp.send_presence(
|
||||||
|
pfrom=presence["to"],
|
||||||
|
ptype="subscribe",
|
||||||
|
pto=user_jid,
|
||||||
|
) # TODO: handle resulting subscribed presences
|
||||||
|
|
||||||
|
async def on_presence_unsubscribe(self, presence: Presence):
|
||||||
|
if presence["to"] == self.xmpp.boundjid.bare:
|
||||||
|
# should we trigger unregistering here?
|
||||||
|
return
|
||||||
|
|
||||||
|
user_jid = presence["from"]
|
||||||
|
user = await self.get_user(presence)
|
||||||
|
if user is None:
|
||||||
|
log.debug("Received remove subscription from unregistered user")
|
||||||
|
if self.needs_registration:
|
||||||
|
return
|
||||||
|
|
||||||
|
await self.api["legacy_contact_remove"](ifrom=user_jid, args=presence["to"])
|
||||||
|
|
||||||
|
for ptype in "unsubscribe", "unsubscribed", "unavailable":
|
||||||
|
self.xmpp.send_presence(
|
||||||
|
pfrom=presence["to"],
|
||||||
|
ptype=ptype,
|
||||||
|
pto=user_jid,
|
||||||
|
)
|
||||||
|
|
||||||
|
async def _legacy_contact_remove(self, jid, node, ifrom, contact_jid: JID):
|
||||||
|
pass
|
||||||
|
|
||||||
|
async def on_message(self, msg: Message):
|
||||||
|
if msg["type"] == "groupchat":
|
||||||
|
return # groupchat messages are out of scope of XEP-0100
|
||||||
|
|
||||||
|
if msg["to"] == self.xmpp.boundjid.bare:
|
||||||
|
# It may be useful to exchange direct messages with the component
|
||||||
|
self.xmpp.event("gateway_message", msg)
|
||||||
|
return
|
||||||
|
|
||||||
|
if self.needs_registration and await self.get_user(msg) is None:
|
||||||
|
return
|
||||||
|
|
||||||
|
self.xmpp.event("legacy_message", msg)
|
||||||
|
|
||||||
|
def transform_legacy_message(
|
||||||
|
self,
|
||||||
|
jabber_user_jid: typing.Union[JID, str],
|
||||||
|
legacy_contact_id: str,
|
||||||
|
body: str,
|
||||||
|
mtype: typing.Optional[str] = None,
|
||||||
|
):
|
||||||
|
"""
|
||||||
|
Transform a legacy message to an XMPP message
|
||||||
|
"""
|
||||||
|
# Should escaping legacy IDs to valid JID local parts be handled here?
|
||||||
|
# Maybe by internal API stuff?
|
||||||
|
self.xmpp.send_message(
|
||||||
|
mfrom=JID(f"{legacy_contact_id}@{self.xmpp.boundjid.bare}"),
|
||||||
|
mto=JID(jabber_user_jid).bare,
|
||||||
|
mbody=body,
|
||||||
|
mtype=mtype,
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
class LegacyError(Exception):
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
log = logging.getLogger(__name__)
|
416
tests/test_stream_xep_0100.py
Normal file
416
tests/test_stream_xep_0100.py
Normal file
@ -0,0 +1,416 @@
|
|||||||
|
import unittest
|
||||||
|
import logging
|
||||||
|
|
||||||
|
from slixmpp import JID
|
||||||
|
from slixmpp.test import SlixTest
|
||||||
|
|
||||||
|
from slixmpp.plugins import xep_0100
|
||||||
|
from slixmpp.plugins.xep_0100 import LegacyError
|
||||||
|
|
||||||
|
|
||||||
|
class TestStreamGateway(SlixTest):
|
||||||
|
def setUp(self):
|
||||||
|
self.stream_start(
|
||||||
|
mode="component",
|
||||||
|
plugins=["xep_0077", "xep_0100"],
|
||||||
|
jid="aim.shakespeare.lit",
|
||||||
|
server="shakespeare.lit",
|
||||||
|
plugin_config={
|
||||||
|
"xep_0100": {"component_name": "AIM Gateway", "type": "aim"}
|
||||||
|
},
|
||||||
|
)
|
||||||
|
|
||||||
|
def next_sent(self):
|
||||||
|
self.wait_for_send_queue()
|
||||||
|
sent = self.xmpp.socket.next_sent(timeout=0.5)
|
||||||
|
if sent is None:
|
||||||
|
return None
|
||||||
|
xml = self.parse_xml(sent)
|
||||||
|
self.fix_namespaces(xml, "jabber:component:accept")
|
||||||
|
sent = self.xmpp._build_stanza(xml, "jabber:component:accept")
|
||||||
|
return sent
|
||||||
|
|
||||||
|
def testDisco(self):
|
||||||
|
# https://xmpp.org/extensions/xep-0100.html#example-3
|
||||||
|
self.recv(
|
||||||
|
"""
|
||||||
|
<iq type='get'
|
||||||
|
from='romeo@montague.lit/orchard'
|
||||||
|
to='aim.shakespeare.lit'
|
||||||
|
id='disco1'>
|
||||||
|
<query xmlns='http://jabber.org/protocol/disco#info'/>
|
||||||
|
</iq>
|
||||||
|
"""
|
||||||
|
)
|
||||||
|
self.send(
|
||||||
|
"""
|
||||||
|
<iq type="result"
|
||||||
|
from="aim.shakespeare.lit"
|
||||||
|
to="romeo@montague.lit/orchard"
|
||||||
|
id="disco1">
|
||||||
|
<query xmlns="http://jabber.org/protocol/disco#info">
|
||||||
|
<identity category="gateway" type="aim" name="AIM Gateway" />
|
||||||
|
<feature var="jabber:iq:register" />
|
||||||
|
<feature var="jabber:x:data" />
|
||||||
|
<feature var="jabber:iq:oob" />
|
||||||
|
<feature var="jabber:x:oob" />
|
||||||
|
</query>
|
||||||
|
</iq>
|
||||||
|
"""
|
||||||
|
)
|
||||||
|
|
||||||
|
def testRegister(self):
|
||||||
|
event_result = {}
|
||||||
|
|
||||||
|
def legacy_login(iq):
|
||||||
|
event_result["user"] = iq["from"]
|
||||||
|
|
||||||
|
self.xmpp.add_event_handler("legacy_login", legacy_login)
|
||||||
|
|
||||||
|
# Jabber User sends IQ-set qualified by the 'jabber:iq:register' namespace to Gateway,
|
||||||
|
# containing information required to register.
|
||||||
|
# https://xmpp.org/extensions/xep-0100.html#example-7
|
||||||
|
self.recv(
|
||||||
|
"""
|
||||||
|
<iq type='set'
|
||||||
|
from='romeo@montague.lit/orchard'
|
||||||
|
to='aim.shakespeare.lit'
|
||||||
|
id='reg2'>
|
||||||
|
<query xmlns='jabber:iq:register'>
|
||||||
|
<username>RomeoMyRomeo</username>
|
||||||
|
<password>ILoveJuliet</password>
|
||||||
|
</query>
|
||||||
|
</iq>
|
||||||
|
"""
|
||||||
|
)
|
||||||
|
# Gateway verifies that registration information provided by Jabber User is valid
|
||||||
|
# (using whatever means appropriate for the Legacy Service) and informs Jabber User of success [A1].
|
||||||
|
# https://xmpp.org/extensions/xep-0100.html#example-8
|
||||||
|
self.send(
|
||||||
|
"""
|
||||||
|
<iq type='result'
|
||||||
|
from='aim.shakespeare.lit'
|
||||||
|
to='romeo@montague.lit/orchard'
|
||||||
|
id='reg2'/>
|
||||||
|
"""
|
||||||
|
)
|
||||||
|
# Gateway sends subscription request to Jabber User (i.e., by sending a presence stanza
|
||||||
|
# of type "subscribe" to Jabber User's bare JID).
|
||||||
|
# https://xmpp.org/extensions/xep-0100.html#example-11
|
||||||
|
sent = self.next_sent()
|
||||||
|
self.check(
|
||||||
|
sent, "/presence@type=subscribe@from=aim.shakespeare.lit", "stanzapath"
|
||||||
|
)
|
||||||
|
self.assertTrue(
|
||||||
|
sent["to"] == "romeo@montague.lit"
|
||||||
|
) # cannot use stanzapath because of @
|
||||||
|
# Jabber User's client SHOULD approve the subscription request (i.e., by sending a presence stanza
|
||||||
|
# of type "subscribed" to Gateway).
|
||||||
|
self.recv(
|
||||||
|
"""
|
||||||
|
<presence type='subscribed'
|
||||||
|
from='romeo@montague.lit'
|
||||||
|
to='aim.shakespeare.lit'/>
|
||||||
|
"""
|
||||||
|
)
|
||||||
|
# Jabber User sends subscription request to Gateway (i.e., by sending a presence stanza
|
||||||
|
# of type "subscribe" to Gateway).
|
||||||
|
self.recv(
|
||||||
|
"""
|
||||||
|
<presence type='subscribe'
|
||||||
|
from='romeo@montague.lit'
|
||||||
|
to='aim.shakespeare.lit'/>
|
||||||
|
"""
|
||||||
|
)
|
||||||
|
# Gateway sends approves subscription request (i.e., by sending a presence stanza of type
|
||||||
|
# "subscribed" to Jabber User's bare JID).
|
||||||
|
sent = self.next_sent()
|
||||||
|
self.check(
|
||||||
|
sent, "/presence@type=subscribed@from=aim.shakespeare.lit", "stanzapath"
|
||||||
|
)
|
||||||
|
self.assertTrue(
|
||||||
|
sent["to"] == "romeo@montague.lit"
|
||||||
|
) # cannot use stanzapath because of @
|
||||||
|
self.assertTrue(
|
||||||
|
self.xmpp.client_roster["romeo@montague.lit"]["subscription"] == "both"
|
||||||
|
)
|
||||||
|
self.recv(
|
||||||
|
"""
|
||||||
|
<presence from='romeo@montague.lit/orchard'
|
||||||
|
to='aim.shakespeare.lit'/>
|
||||||
|
"""
|
||||||
|
)
|
||||||
|
self.assertTrue(event_result["user"] == "romeo@montague.lit/orchard")
|
||||||
|
|
||||||
|
def testBadCredentials(self):
|
||||||
|
def raise_v(*a, **kwa):
|
||||||
|
raise ValueError("Not good")
|
||||||
|
|
||||||
|
self.xmpp["xep_0077"].api.register(raise_v, "user_validate")
|
||||||
|
self.recv(
|
||||||
|
"""
|
||||||
|
<iq type='set'
|
||||||
|
from='romeo@montague.lit/orchard'
|
||||||
|
to='aim.shakespeare.lit'
|
||||||
|
id='reg2'>
|
||||||
|
<query xmlns='jabber:iq:register'>
|
||||||
|
<username>RomeoMyRomeo</username>
|
||||||
|
<password>ILoveJuliet</password>
|
||||||
|
</query>
|
||||||
|
</iq>
|
||||||
|
"""
|
||||||
|
)
|
||||||
|
# xmlns="jabber:client" in error substanza, bug in XEP-0077 plugin or OK?
|
||||||
|
self.send(
|
||||||
|
"""
|
||||||
|
<iq type='error'
|
||||||
|
from='aim.shakespeare.lit'
|
||||||
|
to='romeo@montague.lit/orchard'
|
||||||
|
id='reg2'>
|
||||||
|
<query xmlns='jabber:iq:register'>
|
||||||
|
<username>RomeoMyRomeo</username>
|
||||||
|
<password>ILoveJuliet</password>
|
||||||
|
</query>
|
||||||
|
<error code='406' type='modify' xmlns="jabber:client">
|
||||||
|
<not-acceptable
|
||||||
|
xmlns='urn:ietf:params:xml:ns:xmpp-stanzas'/>
|
||||||
|
<text xmlns="urn:ietf:params:xml:ns:xmpp-stanzas">Not good</text>
|
||||||
|
</error>
|
||||||
|
</iq>
|
||||||
|
""",
|
||||||
|
use_values=False,
|
||||||
|
)
|
||||||
|
|
||||||
|
def testLogin(self):
|
||||||
|
event_result = {}
|
||||||
|
|
||||||
|
def legacy_login(presence):
|
||||||
|
event_result["user"] = presence["from"]
|
||||||
|
|
||||||
|
self.xmpp.add_event_handler("legacy_login", legacy_login)
|
||||||
|
|
||||||
|
self.xmpp["xep_0077"].api["user_validate"](
|
||||||
|
None,
|
||||||
|
None,
|
||||||
|
JID("romeo@montague.lit"),
|
||||||
|
{"username": "RomeoMyRomeo", "password": "ILoveJuliet"},
|
||||||
|
)
|
||||||
|
|
||||||
|
# Jabber User sends available presence broadcast to Server or sends
|
||||||
|
# directed presence to Gateway or a Legacy User.
|
||||||
|
# https://xmpp.org/extensions/xep-0100.html#example-26
|
||||||
|
self.recv(
|
||||||
|
"""
|
||||||
|
<presence from='romeo@montague.lit/orchard'
|
||||||
|
to='juliet@aim.shakespeare.lit'/>
|
||||||
|
<presence from='romeo@montague.lit/orchard'
|
||||||
|
to='aim.shakespeare.lit'/>
|
||||||
|
"""
|
||||||
|
)
|
||||||
|
# Gateway sends presence stanza to Jabber User expressing availability.
|
||||||
|
# https://xmpp.org/extensions/xep-0100.html#example-27
|
||||||
|
self.send(
|
||||||
|
"""
|
||||||
|
<presence from='aim.shakespeare.lit'
|
||||||
|
to='romeo@montague.lit'>
|
||||||
|
<priority>0</priority>
|
||||||
|
</presence>
|
||||||
|
"""
|
||||||
|
)
|
||||||
|
self.assertTrue(event_result["user"] == "romeo@montague.lit/orchard")
|
||||||
|
|
||||||
|
def testLogout(self):
|
||||||
|
self.add_user()
|
||||||
|
event_result = {}
|
||||||
|
|
||||||
|
def legacy_logout(presence):
|
||||||
|
event_result["user"] = presence["from"]
|
||||||
|
|
||||||
|
self.xmpp.add_event_handler("legacy_logout", legacy_logout)
|
||||||
|
# Jabber User sends available presence broadcast to Server or sends
|
||||||
|
# directed presence to Gateway or a Legacy User.
|
||||||
|
# https://xmpp.org/extensions/xep-0100.html#example-32
|
||||||
|
self.recv(
|
||||||
|
"""
|
||||||
|
<presence type='unavailable'
|
||||||
|
from='romeo@montague.lit/orchard'
|
||||||
|
to='aim.shakespeare.lit'/>
|
||||||
|
"""
|
||||||
|
)
|
||||||
|
# Gateway sends presence stanza of type "unavailable" to Jabber User.
|
||||||
|
# https://xmpp.org/extensions/xep-0100.html#example-33
|
||||||
|
self.send(
|
||||||
|
"""
|
||||||
|
<presence type='unavailable'
|
||||||
|
from='aim.shakespeare.lit'
|
||||||
|
to='romeo@montague.lit/orchard'>
|
||||||
|
<priority>0</priority>
|
||||||
|
</presence>
|
||||||
|
"""
|
||||||
|
)
|
||||||
|
self.assertTrue(event_result["user"] == "romeo@montague.lit/orchard")
|
||||||
|
|
||||||
|
def testAddContact(self):
|
||||||
|
self.add_user()
|
||||||
|
# Had to lowercase capuletnurse everywhere
|
||||||
|
# Jabber User sends presence stanza of type "subscribe" to Legacy User.
|
||||||
|
self.recv(
|
||||||
|
"""
|
||||||
|
<presence type='subscribe'
|
||||||
|
from='romeo@montague.lit'
|
||||||
|
to='capuletnurse@aim.shakespeare.lit'/>
|
||||||
|
"""
|
||||||
|
)
|
||||||
|
# If Legacy User approves subscription request, Gateway sends presence stanza of
|
||||||
|
# type "subscribed" to Jabber User on behalf of Legacy User. [A1]
|
||||||
|
self.send(
|
||||||
|
"""
|
||||||
|
<presence type='subscribed'
|
||||||
|
from='capuletnurse@aim.shakespeare.lit'
|
||||||
|
to='romeo@montague.lit'/>
|
||||||
|
"""
|
||||||
|
)
|
||||||
|
# Had to remove the resource here
|
||||||
|
self.send(
|
||||||
|
"""
|
||||||
|
<presence from='capuletnurse@aim.shakespeare.lit'
|
||||||
|
to='romeo@montague.lit'/>
|
||||||
|
"""
|
||||||
|
)
|
||||||
|
self.send(
|
||||||
|
"""
|
||||||
|
<presence type='subscribe'
|
||||||
|
from='capuletnurse@aim.shakespeare.lit'
|
||||||
|
to='romeo@montague.lit'/>
|
||||||
|
"""
|
||||||
|
)
|
||||||
|
self.recv(
|
||||||
|
"""
|
||||||
|
<presence type='subscribed'
|
||||||
|
from='romeo@montague.lit'
|
||||||
|
to='capuletnurse@aim.shakespeare.lit'/>
|
||||||
|
"""
|
||||||
|
)
|
||||||
|
|
||||||
|
def testAddContactFail(self):
|
||||||
|
self.add_user()
|
||||||
|
res = {}
|
||||||
|
async def legacy_contact_add(jid, node, ifrom, contact_jid):
|
||||||
|
res.update(**locals())
|
||||||
|
raise LegacyError
|
||||||
|
self.xmpp["xep_0100"].api.register(
|
||||||
|
legacy_contact_add, "legacy_contact_add"
|
||||||
|
)
|
||||||
|
self.recv(
|
||||||
|
"""
|
||||||
|
<presence type='subscribe'
|
||||||
|
from='romeo@montague.lit'
|
||||||
|
to='juliet@aim.shakespeare.lit'/>
|
||||||
|
"""
|
||||||
|
)
|
||||||
|
self.send(
|
||||||
|
"""
|
||||||
|
<presence type='unsubscribed'
|
||||||
|
from='juliet@aim.shakespeare.lit'
|
||||||
|
to='romeo@montague.lit'/>
|
||||||
|
"""
|
||||||
|
)
|
||||||
|
self.assertTrue(res["ifrom"] == "romeo@montague.lit")
|
||||||
|
self.assertTrue(res["contact_jid"] == "juliet@aim.shakespeare.lit")
|
||||||
|
|
||||||
|
|
||||||
|
def testRemoveContact(self):
|
||||||
|
self.add_user()
|
||||||
|
result = {}
|
||||||
|
# Jabber User sends IQ-set qualified by the 'jabber:iq:roster' namespace, containing subscription
|
||||||
|
# attribute with value of "remove".
|
||||||
|
async def legacy_contact_remove(jid, node, ifrom, contact_jid):
|
||||||
|
result.update(**locals())
|
||||||
|
|
||||||
|
self.xmpp["xep_0100"].api.register(
|
||||||
|
legacy_contact_remove, "legacy_contact_remove"
|
||||||
|
)
|
||||||
|
|
||||||
|
# Jabber User sends IQ-set qualified by the 'jabber:iq:roster' namespace, containing subscription
|
||||||
|
# attribute with value of "remove".
|
||||||
|
self.recv( # server sends this
|
||||||
|
"""
|
||||||
|
<presence type='unsubscribe'
|
||||||
|
to='capuletnurse@aim.shakespeare.lit'
|
||||||
|
from='romeo@montague.lit'/>
|
||||||
|
"""
|
||||||
|
)
|
||||||
|
for ptype in "unsubscribe", "unsubscribed", "unavailable":
|
||||||
|
self.send( # server sends this
|
||||||
|
f"""
|
||||||
|
<presence type='{ptype}'
|
||||||
|
from='capuletnurse@aim.shakespeare.lit'
|
||||||
|
to='romeo@montague.lit'/>
|
||||||
|
"""
|
||||||
|
)
|
||||||
|
|
||||||
|
self.assertTrue(result["ifrom"] == "romeo@montague.lit")
|
||||||
|
self.assertTrue(
|
||||||
|
result["contact_jid"] == JID("CapuletNurse@aim.shakespeare.lit")
|
||||||
|
)
|
||||||
|
|
||||||
|
def testSendMessage(self):
|
||||||
|
self.xmpp["xep_0100"].transform_legacy_message(
|
||||||
|
jabber_user_jid="romeo@montague.lit",
|
||||||
|
legacy_contact_id="juliet",
|
||||||
|
body="Art thou not Romeo, and a Montague?",
|
||||||
|
)
|
||||||
|
self.send(
|
||||||
|
"""
|
||||||
|
<message from='juliet@aim.shakespeare.lit'
|
||||||
|
to='romeo@montague.lit'>
|
||||||
|
<body>Art thou not Romeo, and a Montague?</body>
|
||||||
|
</message>
|
||||||
|
"""
|
||||||
|
)
|
||||||
|
|
||||||
|
def testLegacyMessage(self):
|
||||||
|
self.add_user()
|
||||||
|
result = {}
|
||||||
|
|
||||||
|
def legacy_message(msg):
|
||||||
|
result["msg"] = msg
|
||||||
|
|
||||||
|
self.xmpp.add_event_handler("legacy_message", legacy_message)
|
||||||
|
self.recv(
|
||||||
|
"""
|
||||||
|
<message to='juliet@aim.shakespeare.lit'
|
||||||
|
from='romeo@montague.lit'>
|
||||||
|
<body>Something shakespearian</body>
|
||||||
|
</message>
|
||||||
|
"""
|
||||||
|
)
|
||||||
|
self.wait_for_send_queue()
|
||||||
|
self.assertTrue(result["msg"]["from"] == "romeo@montague.lit")
|
||||||
|
self.assertTrue(result["msg"]["to"] == "juliet@aim.shakespeare.lit")
|
||||||
|
|
||||||
|
def testPluginEnd(self):
|
||||||
|
exc = False
|
||||||
|
try:
|
||||||
|
self.xmpp.plugin.disable("xep_0100")
|
||||||
|
except Exception:
|
||||||
|
exc = True
|
||||||
|
self.assertFalse(exc)
|
||||||
|
|
||||||
|
def add_user(self):
|
||||||
|
self.xmpp.loop.run_until_complete(
|
||||||
|
self.xmpp["xep_0077"].api["user_validate"](
|
||||||
|
None,
|
||||||
|
None,
|
||||||
|
JID("romeo@montague.lit"),
|
||||||
|
{"username": "RomeoMyRomeo", "password": "ILoveJuliet"},
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
# TODO: edit reg
|
||||||
|
# TODO: unregister
|
||||||
|
# TODO: login fails
|
||||||
|
|
||||||
|
|
||||||
|
logging.basicConfig(level=logging.DEBUG)
|
||||||
|
suite = unittest.TestLoader().loadTestsFromTestCase(TestStreamGateway)
|
Loading…
Reference in New Issue
Block a user