plugin XEP-0384, xml_tools: avoid getItems timeout + fix empty node crash + parsing:
- use `max_items` in `getItems` calls for bundles, as otherwise some pubsub service may return full nodes, which may be huge is `max_items=1` is not set on the node, possibly resulting in timeouts. - the plugin was crashing when TWOMEMO devices list node has no items at all. This is not the case anymore. - a naive parsing method has been implemented in `xml_tools` to replace the serialisation/deserialisation method. This should be more efficient and will avoid annoying `ns0:` prefixes in XML logs.
This commit is contained in:
parent
ee5784811e
commit
8444115262
2 changed files with 71 additions and 17 deletions
|
|
@ -419,7 +419,7 @@ async def download_oldmemo_bundle(
|
|||
node = f"eu.siacs.conversations.axolotl.bundles:{device_id}"
|
||||
|
||||
try:
|
||||
items, __ = await xep_0060.getItems(client, jid.JID(bare_jid), node)
|
||||
items, __ = await xep_0060.getItems(client, jid.JID(bare_jid), node, max_items=1)
|
||||
except Exception as e:
|
||||
raise omemo.BundleDownloadFailed(
|
||||
f"Bundle download failed for {bare_jid}: {device_id} under namespace"
|
||||
|
|
@ -432,7 +432,7 @@ async def download_oldmemo_bundle(
|
|||
f" {namespace}: Unexpected number of items retrieved: {len(items)}."
|
||||
)
|
||||
|
||||
element = next(iter(domish_to_etree(cast(domish.Element, items[0]))), None)
|
||||
element = next(iter(xml_tools.domish_elt_2_et_elt(cast(domish.Element, items[0]))), None)
|
||||
if element is None:
|
||||
raise omemo.BundleDownloadFailed(
|
||||
f"Bundle download failed for {bare_jid}: {device_id} under namespace"
|
||||
|
|
@ -476,7 +476,7 @@ def make_session_manager(sat: SAT, profile: str) -> Type[omemo.SessionManager]:
|
|||
client,
|
||||
client.jid.userhostJID(),
|
||||
node,
|
||||
etree_to_domish(element),
|
||||
xml_tools.et_elt_2_domish_elt(element),
|
||||
item_id=str(bundle.device_id),
|
||||
extra={
|
||||
xep_0060.EXTRA_PUBLISH_OPTIONS: {
|
||||
|
|
@ -516,7 +516,7 @@ def make_session_manager(sat: SAT, profile: str) -> Type[omemo.SessionManager]:
|
|||
client,
|
||||
client.jid.userhostJID(),
|
||||
node,
|
||||
etree_to_domish(element),
|
||||
xml_tools.et_elt_2_domish_elt(element),
|
||||
item_id=xep_0060.ID_SINGLETON,
|
||||
extra={
|
||||
xep_0060.EXTRA_PUBLISH_OPTIONS: { xep_0060.OPT_MAX_ITEMS: 1 },
|
||||
|
|
@ -563,7 +563,7 @@ def make_session_manager(sat: SAT, profile: str) -> Type[omemo.SessionManager]:
|
|||
)
|
||||
|
||||
element = next(
|
||||
iter(domish_to_etree(cast(domish.Element, items[0]))),
|
||||
iter(xml_tools.domish_elt_2_et_elt(cast(domish.Element, items[0]))),
|
||||
None
|
||||
)
|
||||
if element is None:
|
||||
|
|
@ -650,7 +650,7 @@ def make_session_manager(sat: SAT, profile: str) -> Type[omemo.SessionManager]:
|
|||
client,
|
||||
client.jid.userhostJID(),
|
||||
node,
|
||||
etree_to_domish(element),
|
||||
xml_tools.et_elt_2_domish_elt(element),
|
||||
item_id=xep_0060.ID_SINGLETON,
|
||||
extra={
|
||||
xep_0060.EXTRA_PUBLISH_OPTIONS: {
|
||||
|
|
@ -705,13 +705,15 @@ def make_session_manager(sat: SAT, profile: str) -> Type[omemo.SessionManager]:
|
|||
f" {namespace}"
|
||||
) from e
|
||||
|
||||
if len(items) != 1:
|
||||
if len(items) == 0:
|
||||
return {}
|
||||
elif len(items) != 1:
|
||||
raise omemo.DeviceListDownloadFailed(
|
||||
f"Device list download failed for {bare_jid} under namespace"
|
||||
f" {namespace}: Unexpected number of items retrieved: {len(items)}."
|
||||
)
|
||||
|
||||
element = next(iter(domish_to_etree(cast(domish.Element, items[0]))), None)
|
||||
element = next(iter(xml_tools.domish_elt_2_et_elt(cast(domish.Element, items[0]))), None)
|
||||
if element is None:
|
||||
raise omemo.DeviceListDownloadFailed(
|
||||
f"Device list download failed for {bare_jid} under namespace"
|
||||
|
|
@ -866,10 +868,10 @@ def make_session_manager(sat: SAT, profile: str) -> Type[omemo.SessionManager]:
|
|||
"timestamp": time.time()
|
||||
}))
|
||||
|
||||
message_data["xml"].addChild(etree_to_domish(element))
|
||||
message_data["xml"].addChild(xml_tools.et_elt_2_domish_elt(element))
|
||||
|
||||
try:
|
||||
await client.send(message_data["xml"])
|
||||
await client.a_send(message_data["xml"])
|
||||
except Exception as e:
|
||||
raise omemo.MessageSendingFailed() from e
|
||||
|
||||
|
|
@ -1493,7 +1495,6 @@ class OMEMO:
|
|||
encrypted.
|
||||
@return: Whether to continue the message received flow.
|
||||
"""
|
||||
|
||||
muc_plaintext_cache_key: Optional[MUCPlaintextCacheKey] = None
|
||||
|
||||
sender_jid = jid.JID(message_elt["from"])
|
||||
|
|
@ -1583,7 +1584,7 @@ class OMEMO:
|
|||
if twomemo_encrypted_elt is not None:
|
||||
try:
|
||||
message = twomemo.etree.parse_message(
|
||||
domish_to_etree(twomemo_encrypted_elt),
|
||||
xml_tools.domish_elt_2_et_elt(twomemo_encrypted_elt),
|
||||
sender_bare_jid
|
||||
)
|
||||
except (ValueError, XMLSchemaValidationError):
|
||||
|
|
@ -1597,7 +1598,7 @@ class OMEMO:
|
|||
if oldmemo_encrypted_elt is not None:
|
||||
try:
|
||||
message = await oldmemo.etree.parse_message(
|
||||
domish_to_etree(oldmemo_encrypted_elt),
|
||||
xml_tools.domish_elt_2_et_elt(oldmemo_encrypted_elt),
|
||||
sender_bare_jid,
|
||||
client.jid.userhost(),
|
||||
session_manager
|
||||
|
|
@ -1999,11 +2000,11 @@ class OMEMO:
|
|||
|
||||
if namespace == twomemo.twomemo.NAMESPACE:
|
||||
# Add the encrypted element
|
||||
stanza.addChild(etree_to_domish(twomemo.etree.serialize_message(message)))
|
||||
stanza.addChild(xml_tools.et_elt_2_domish_elt(twomemo.etree.serialize_message(message)))
|
||||
|
||||
if namespace == oldmemo.oldmemo.NAMESPACE:
|
||||
# Add the encrypted element
|
||||
stanza.addChild(etree_to_domish(oldmemo.etree.serialize_message(message)))
|
||||
stanza.addChild(xml_tools.et_elt_2_domish_elt(oldmemo.etree.serialize_message(message)))
|
||||
|
||||
if muc_plaintext_cache_key is not None:
|
||||
self.__muc_plaintext_cache[muc_plaintext_cache_key] = plaintext
|
||||
|
|
@ -2031,7 +2032,7 @@ class OMEMO:
|
|||
log.debug("Ignoring empty device list update.")
|
||||
return
|
||||
|
||||
item_elt = domish_to_etree(item)
|
||||
item_elt = xml_tools.domish_elt_2_et_elt(item)
|
||||
|
||||
device_list: Dict[int, Optional[str]] = {}
|
||||
namespace: Optional[str] = None
|
||||
|
|
|
|||
|
|
@ -18,7 +18,7 @@
|
|||
|
||||
|
||||
import re
|
||||
from typing import Optional
|
||||
from typing import Optional, Tuple
|
||||
import html.entities
|
||||
from collections import OrderedDict
|
||||
from xml.dom import minidom, NotFoundErr
|
||||
|
|
@ -30,6 +30,7 @@ from sat.core import exceptions
|
|||
from sat.core.i18n import _
|
||||
from sat.core.constants import Const as C
|
||||
from sat.core.log import getLogger
|
||||
import xml.etree.ElementTree as ET
|
||||
|
||||
|
||||
log = getLogger(__name__)
|
||||
|
|
@ -1957,3 +1958,55 @@ def pFmtElt(elt, indent=0, defaultUri=""):
|
|||
def ppElt(elt):
|
||||
"""Pretty print a domish.Element"""
|
||||
print(pFmtElt(elt))
|
||||
|
||||
|
||||
# ElementTree
|
||||
|
||||
def et_get_namespace_and_name(et_elt: ET.Element) -> Tuple[Optional[str], str]:
|
||||
"""Retrieve element namespace and name from ElementTree element
|
||||
|
||||
@param et_elt: ElementTree element
|
||||
@return: namespace and name of the element
|
||||
if not namespace if specified, None is returned
|
||||
"""
|
||||
name = et_elt.tag
|
||||
if not name:
|
||||
raise ValueError("no name set in ET element")
|
||||
elif name[0] != "{":
|
||||
return None, name
|
||||
end_idx = name.find("}")
|
||||
if end_idx == -1:
|
||||
raise ValueError("Invalid ET name")
|
||||
return name[1:end_idx], name[end_idx+1:]
|
||||
|
||||
|
||||
def et_elt_2_domish_elt(et_elt: ET.Element) -> domish.Element:
|
||||
"""Convert ElementTree element to Twisted's domish.Element
|
||||
|
||||
Note: this is a naive implementation, adapted to XMPP, and some content are ignored
|
||||
(attributes namespaces, tail)
|
||||
"""
|
||||
namespace, name = et_get_namespace_and_name(et_elt)
|
||||
elt = domish.Element((namespace, name), attribs=et_elt.attrib)
|
||||
if et_elt.text:
|
||||
elt.addContent(et_elt.text)
|
||||
for child in et_elt:
|
||||
elt.addChild(et_elt_2_domish_elt(child))
|
||||
return elt
|
||||
|
||||
|
||||
def domish_elt_2_et_elt(elt: domish.Element) -> ET.Element:
|
||||
"""Convert Twisted's domish.Element to ElementTree equivalent
|
||||
|
||||
Note: this is a naive implementation, adapter to XMPP, and some text content may be
|
||||
missing (content put after a tag, i.e. what would go to the "tail" attribute of ET
|
||||
Element)
|
||||
"""
|
||||
tag = f"{{{elt.uri}}}{elt.name}" if elt.uri else elt.name
|
||||
et_elt = ET.Element(tag, attrib=elt.attributes)
|
||||
content = str(elt)
|
||||
if content:
|
||||
et_elt.text = str(elt)
|
||||
for child in elt.elements():
|
||||
et_elt.append(domish_elt_2_et_elt(child))
|
||||
return et_elt
|
||||
|
|
|
|||
Loading…
Reference in a new issue