Mercurial > libervia-backend
comparison sat/plugins/plugin_xep_0384.py @ 3914:4cb38c8312a1
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.
author | Goffi <goffi@goffi.org> |
---|---|
date | Sat, 24 Sep 2022 16:37:46 +0200 |
parents | 8289ac1b34f4 |
children | e63f96e60f7b |
comparison
equal
deleted
inserted
replaced
3913:944f51f9c2b4 | 3914:4cb38c8312a1 |
---|---|
417 | 417 |
418 namespace = oldmemo.oldmemo.NAMESPACE | 418 namespace = oldmemo.oldmemo.NAMESPACE |
419 node = f"eu.siacs.conversations.axolotl.bundles:{device_id}" | 419 node = f"eu.siacs.conversations.axolotl.bundles:{device_id}" |
420 | 420 |
421 try: | 421 try: |
422 items, __ = await xep_0060.getItems(client, jid.JID(bare_jid), node) | 422 items, __ = await xep_0060.getItems(client, jid.JID(bare_jid), node, max_items=1) |
423 except Exception as e: | 423 except Exception as e: |
424 raise omemo.BundleDownloadFailed( | 424 raise omemo.BundleDownloadFailed( |
425 f"Bundle download failed for {bare_jid}: {device_id} under namespace" | 425 f"Bundle download failed for {bare_jid}: {device_id} under namespace" |
426 f" {namespace}" | 426 f" {namespace}" |
427 ) from e | 427 ) from e |
430 raise omemo.BundleDownloadFailed( | 430 raise omemo.BundleDownloadFailed( |
431 f"Bundle download failed for {bare_jid}: {device_id} under namespace" | 431 f"Bundle download failed for {bare_jid}: {device_id} under namespace" |
432 f" {namespace}: Unexpected number of items retrieved: {len(items)}." | 432 f" {namespace}: Unexpected number of items retrieved: {len(items)}." |
433 ) | 433 ) |
434 | 434 |
435 element = next(iter(domish_to_etree(cast(domish.Element, items[0]))), None) | 435 element = next(iter(xml_tools.domish_elt_2_et_elt(cast(domish.Element, items[0]))), None) |
436 if element is None: | 436 if element is None: |
437 raise omemo.BundleDownloadFailed( | 437 raise omemo.BundleDownloadFailed( |
438 f"Bundle download failed for {bare_jid}: {device_id} under namespace" | 438 f"Bundle download failed for {bare_jid}: {device_id} under namespace" |
439 f" {namespace}: Item download succeeded but parsing failed: {element}." | 439 f" {namespace}: Item download succeeded but parsing failed: {element}." |
440 ) | 440 ) |
474 try: | 474 try: |
475 await xep_0060.sendItem( | 475 await xep_0060.sendItem( |
476 client, | 476 client, |
477 client.jid.userhostJID(), | 477 client.jid.userhostJID(), |
478 node, | 478 node, |
479 etree_to_domish(element), | 479 xml_tools.et_elt_2_domish_elt(element), |
480 item_id=str(bundle.device_id), | 480 item_id=str(bundle.device_id), |
481 extra={ | 481 extra={ |
482 xep_0060.EXTRA_PUBLISH_OPTIONS: { | 482 xep_0060.EXTRA_PUBLISH_OPTIONS: { |
483 xep_0060.OPT_MAX_ITEMS: "max" | 483 xep_0060.OPT_MAX_ITEMS: "max" |
484 }, | 484 }, |
514 try: | 514 try: |
515 await xep_0060.sendItem( | 515 await xep_0060.sendItem( |
516 client, | 516 client, |
517 client.jid.userhostJID(), | 517 client.jid.userhostJID(), |
518 node, | 518 node, |
519 etree_to_domish(element), | 519 xml_tools.et_elt_2_domish_elt(element), |
520 item_id=xep_0060.ID_SINGLETON, | 520 item_id=xep_0060.ID_SINGLETON, |
521 extra={ | 521 extra={ |
522 xep_0060.EXTRA_PUBLISH_OPTIONS: { xep_0060.OPT_MAX_ITEMS: 1 }, | 522 xep_0060.EXTRA_PUBLISH_OPTIONS: { xep_0060.OPT_MAX_ITEMS: 1 }, |
523 xep_0060.EXTRA_ON_PRECOND_NOT_MET: "publish_without_options" | 523 xep_0060.EXTRA_ON_PRECOND_NOT_MET: "publish_without_options" |
524 } | 524 } |
561 f" namespace {namespace}: Unexpected number of items retrieved:" | 561 f" namespace {namespace}: Unexpected number of items retrieved:" |
562 f" {len(items)}." | 562 f" {len(items)}." |
563 ) | 563 ) |
564 | 564 |
565 element = next( | 565 element = next( |
566 iter(domish_to_etree(cast(domish.Element, items[0]))), | 566 iter(xml_tools.domish_elt_2_et_elt(cast(domish.Element, items[0]))), |
567 None | 567 None |
568 ) | 568 ) |
569 if element is None: | 569 if element is None: |
570 raise omemo.BundleDownloadFailed( | 570 raise omemo.BundleDownloadFailed( |
571 f"Bundle download failed for {bare_jid}: {device_id} under" | 571 f"Bundle download failed for {bare_jid}: {device_id} under" |
648 try: | 648 try: |
649 await xep_0060.sendItem( | 649 await xep_0060.sendItem( |
650 client, | 650 client, |
651 client.jid.userhostJID(), | 651 client.jid.userhostJID(), |
652 node, | 652 node, |
653 etree_to_domish(element), | 653 xml_tools.et_elt_2_domish_elt(element), |
654 item_id=xep_0060.ID_SINGLETON, | 654 item_id=xep_0060.ID_SINGLETON, |
655 extra={ | 655 extra={ |
656 xep_0060.EXTRA_PUBLISH_OPTIONS: { | 656 xep_0060.EXTRA_PUBLISH_OPTIONS: { |
657 xep_0060.OPT_MAX_ITEMS: 1, | 657 xep_0060.OPT_MAX_ITEMS: 1, |
658 xep_0060.OPT_ACCESS_MODEL: "open" | 658 xep_0060.OPT_ACCESS_MODEL: "open" |
703 raise omemo.DeviceListDownloadFailed( | 703 raise omemo.DeviceListDownloadFailed( |
704 f"Device list download failed for {bare_jid} under namespace" | 704 f"Device list download failed for {bare_jid} under namespace" |
705 f" {namespace}" | 705 f" {namespace}" |
706 ) from e | 706 ) from e |
707 | 707 |
708 if len(items) != 1: | 708 if len(items) == 0: |
709 return {} | |
710 elif len(items) != 1: | |
709 raise omemo.DeviceListDownloadFailed( | 711 raise omemo.DeviceListDownloadFailed( |
710 f"Device list download failed for {bare_jid} under namespace" | 712 f"Device list download failed for {bare_jid} under namespace" |
711 f" {namespace}: Unexpected number of items retrieved: {len(items)}." | 713 f" {namespace}: Unexpected number of items retrieved: {len(items)}." |
712 ) | 714 ) |
713 | 715 |
714 element = next(iter(domish_to_etree(cast(domish.Element, items[0]))), None) | 716 element = next(iter(xml_tools.domish_elt_2_et_elt(cast(domish.Element, items[0]))), None) |
715 if element is None: | 717 if element is None: |
716 raise omemo.DeviceListDownloadFailed( | 718 raise omemo.DeviceListDownloadFailed( |
717 f"Device list download failed for {bare_jid} under namespace" | 719 f"Device list download failed for {bare_jid} under namespace" |
718 f" {namespace}: Item download succeeded but parsing failed:" | 720 f" {namespace}: Item download succeeded but parsing failed:" |
719 f" {element}." | 721 f" {element}." |
864 "type": C.MESS_TYPE_CHAT, | 866 "type": C.MESS_TYPE_CHAT, |
865 "extra": {}, | 867 "extra": {}, |
866 "timestamp": time.time() | 868 "timestamp": time.time() |
867 })) | 869 })) |
868 | 870 |
869 message_data["xml"].addChild(etree_to_domish(element)) | 871 message_data["xml"].addChild(xml_tools.et_elt_2_domish_elt(element)) |
870 | 872 |
871 try: | 873 try: |
872 await client.send(message_data["xml"]) | 874 await client.a_send(message_data["xml"]) |
873 except Exception as e: | 875 except Exception as e: |
874 raise omemo.MessageSendingFailed() from e | 876 raise omemo.MessageSendingFailed() from e |
875 | 877 |
876 async def __prompt_manual_trust( | 878 async def __prompt_manual_trust( |
877 self, | 879 self, |
1491 message has fully progressed through the message receiving flow. Can be used | 1493 message has fully progressed through the message receiving flow. Can be used |
1492 to apply treatments to the fully processed message, like marking it as | 1494 to apply treatments to the fully processed message, like marking it as |
1493 encrypted. | 1495 encrypted. |
1494 @return: Whether to continue the message received flow. | 1496 @return: Whether to continue the message received flow. |
1495 """ | 1497 """ |
1496 | |
1497 muc_plaintext_cache_key: Optional[MUCPlaintextCacheKey] = None | 1498 muc_plaintext_cache_key: Optional[MUCPlaintextCacheKey] = None |
1498 | 1499 |
1499 sender_jid = jid.JID(message_elt["from"]) | 1500 sender_jid = jid.JID(message_elt["from"]) |
1500 feedback_jid: jid.JID | 1501 feedback_jid: jid.JID |
1501 | 1502 |
1581 session_manager = await self.__prepare_for_profile(cast(str, client.profile)) | 1582 session_manager = await self.__prepare_for_profile(cast(str, client.profile)) |
1582 | 1583 |
1583 if twomemo_encrypted_elt is not None: | 1584 if twomemo_encrypted_elt is not None: |
1584 try: | 1585 try: |
1585 message = twomemo.etree.parse_message( | 1586 message = twomemo.etree.parse_message( |
1586 domish_to_etree(twomemo_encrypted_elt), | 1587 xml_tools.domish_elt_2_et_elt(twomemo_encrypted_elt), |
1587 sender_bare_jid | 1588 sender_bare_jid |
1588 ) | 1589 ) |
1589 except (ValueError, XMLSchemaValidationError): | 1590 except (ValueError, XMLSchemaValidationError): |
1590 log.warning( | 1591 log.warning( |
1591 f"Ingoring malformed encrypted message for namespace" | 1592 f"Ingoring malformed encrypted message for namespace" |
1595 encrypted_elt = twomemo_encrypted_elt | 1596 encrypted_elt = twomemo_encrypted_elt |
1596 | 1597 |
1597 if oldmemo_encrypted_elt is not None: | 1598 if oldmemo_encrypted_elt is not None: |
1598 try: | 1599 try: |
1599 message = await oldmemo.etree.parse_message( | 1600 message = await oldmemo.etree.parse_message( |
1600 domish_to_etree(oldmemo_encrypted_elt), | 1601 xml_tools.domish_elt_2_et_elt(oldmemo_encrypted_elt), |
1601 sender_bare_jid, | 1602 sender_bare_jid, |
1602 client.jid.userhost(), | 1603 client.jid.userhost(), |
1603 session_manager | 1604 session_manager |
1604 ) | 1605 ) |
1605 except (ValueError, XMLSchemaValidationError): | 1606 except (ValueError, XMLSchemaValidationError): |
1997 | 1998 |
1998 message = next(message for message in messages if message.namespace == namespace) | 1999 message = next(message for message in messages if message.namespace == namespace) |
1999 | 2000 |
2000 if namespace == twomemo.twomemo.NAMESPACE: | 2001 if namespace == twomemo.twomemo.NAMESPACE: |
2001 # Add the encrypted element | 2002 # Add the encrypted element |
2002 stanza.addChild(etree_to_domish(twomemo.etree.serialize_message(message))) | 2003 stanza.addChild(xml_tools.et_elt_2_domish_elt(twomemo.etree.serialize_message(message))) |
2003 | 2004 |
2004 if namespace == oldmemo.oldmemo.NAMESPACE: | 2005 if namespace == oldmemo.oldmemo.NAMESPACE: |
2005 # Add the encrypted element | 2006 # Add the encrypted element |
2006 stanza.addChild(etree_to_domish(oldmemo.etree.serialize_message(message))) | 2007 stanza.addChild(xml_tools.et_elt_2_domish_elt(oldmemo.etree.serialize_message(message))) |
2007 | 2008 |
2008 if muc_plaintext_cache_key is not None: | 2009 if muc_plaintext_cache_key is not None: |
2009 self.__muc_plaintext_cache[muc_plaintext_cache_key] = plaintext | 2010 self.__muc_plaintext_cache[muc_plaintext_cache_key] = plaintext |
2010 | 2011 |
2011 async def __on_device_list_update( | 2012 async def __on_device_list_update( |
2029 item = next(iter(items), None) | 2030 item = next(iter(items), None) |
2030 if item is None: | 2031 if item is None: |
2031 log.debug("Ignoring empty device list update.") | 2032 log.debug("Ignoring empty device list update.") |
2032 return | 2033 return |
2033 | 2034 |
2034 item_elt = domish_to_etree(item) | 2035 item_elt = xml_tools.domish_elt_2_et_elt(item) |
2035 | 2036 |
2036 device_list: Dict[int, Optional[str]] = {} | 2037 device_list: Dict[int, Optional[str]] = {} |
2037 namespace: Optional[str] = None | 2038 namespace: Optional[str] = None |
2038 | 2039 |
2039 list_elt = item_elt.find(f"{{{twomemo.twomemo.NAMESPACE}}}devices") | 2040 list_elt = item_elt.find(f"{{{twomemo.twomemo.NAMESPACE}}}devices") |