# HG changeset patch # User Goffi # Date 1551376646 -3600 # Node ID 0ab62dd3cf057325a5bd63efcdfd2bc78f22bdde # Parent 114cdde9ff96800bc1f9b8b1831d52650104931a plugin XEP-0384: better bundle handling + misc improvments - public bundles are now retrieved only when needed (i.e. on MissingBundleException when encrypting a message) - a feedback is given to user when a message is received but it has not been encrypted for our device - fixed feedback on decryption error if message come from an other device of the client (i.e. it's a carbon copy) - setBundle if called in a "finally" if republish_bundle is set after decrypting a message diff -r 114cdde9ff96 -r 0ab62dd3cf05 sat/plugins/plugin_xep_0384.py --- a/sat/plugins/plugin_xep_0384.py Thu Feb 28 18:57:06 2019 +0100 +++ b/sat/plugins/plugin_xep_0384.py Thu Feb 28 18:57:26 2019 +0100 @@ -783,35 +783,12 @@ @defer.inlineCallbacks def onNewDevices(self, itemsEvent, profile): client = self.host.getClient(profile) - cache = client._xep_0384_cache omemo_session = client._xep_0384_session entity = itemsEvent.sender - entity_cache = cache.setdefault(entity, {}) + devices = self.parseDevices(itemsEvent.items) omemo_session.newDeviceList(entity, devices) - missing_devices = devices.difference(entity_cache.keys()) - if missing_devices: - bundles, bundles_not_found = yield self.getBundles( - client, entity, missing_devices) - entity_cache.update(bundles) - if bundles_not_found and entity == client.jid.userhostJID(): - # we have devices announced in our own public list - # with missing bundles - own_device = client._xep_0384_device_id - if own_device in bundles_not_found: - log.warning(_(u"Our own device has no attached bundle, fixing it")) - bundles_not_found.remove(own_device) - yield self.setBundle(client, omemo_session.public_bundle, own_device) - if bundles_not_found: - # some announced devices have no bundle, we update our public - # list to remove missing devices. - log.warning(_( - u"Some devices have missing bundles, cleaning out public " - u"devices list")) - existing_devices = devices - bundles_not_found - yield self.setDevices(client, existing_devices) - # we check that our device has not been removed from the list if entity == client.jid.userhostJID(): own_device = client._xep_0384_device_id if own_device not in devices: @@ -819,7 +796,6 @@ devices.add(own_device) yield self.setDevices(client, devices) - ## triggers @defer.inlineCallbacks @@ -837,13 +813,44 @@ """ # FIXME: not all problems are handled yet untrusted = {} + missing_bundles = {} expect_problems = {} + cache = client._xep_0384_cache for problem in problems: if isinstance(problem, omemo_excpt.UntrustedException): untrusted[unicode(hash(problem))] = problem + if isinstance(problem, omemo_excpt.MissingBundleException): + pb_entity = jid.JID(problem.bare_jid) + entity_cache = cache.setdefault(pb_entity, {}) + entity_bundles = bundles.setdefault(pb_entity, {}) + if problem.device in entity_cache: + entity_bundles[problem.device] = entity_cache[problem.device] + else: + found_bundles, missing = yield self.getBundles( + client, pb_entity, [problem.device]) + entity_cache.update(bundles) + entity_bundles.update(found_bundles) + if problem.device in missing: + missing_bundles.setdefault(pb_entity, set()).add( + problem.device) + expect_problems.setdefault(problem.bare_jid, set()).add( + problem.device) elif isinstance(problem, omemo_excpt.NoEligibleDevicesException): pass + for peer_jid, devices in missing_bundles.iteritems(): + devices_s = [unicode(d) for d in devices] + log.warning( + _(u"Can't retrieve bundle for device(s) {devices} of entity {peer}, " + u"the message will not be readable on this/those device(s)").format( + devices=u", ".join(devices_s), peer=peer_jid.full())) + client.feedback( + entity, + D_(u"You're destinee {peer} has missing encryption data on some of " + u"his/her device(s) (bundle on device {devices}), the message won't " + u"be readable on this/those device.").format( + peer=peer_jid.full(), devices=u", ".join(devices_s))) + if untrusted: trust_data = {} for trust_id, data in untrusted.iteritems(): @@ -873,38 +880,16 @@ @defer.inlineCallbacks def encryptMessage(self, client, entity_bare_jid, message): omemo_session = client._xep_0384_session - cache = client._xep_0384_cache - try: - bundles = {entity_bare_jid: cache[entity_bare_jid]} - except KeyError: - # No devices know for this entity, let try to find them. - # This can happen if the entity is not in our roster, or doesn't handle OMEMO - # or if we haven't received the devices from PEP yet. - try: - devices = yield self.getDevices(client, entity_bare_jid) - bundles, __ = yield self.getBundles(client, entity_bare_jid, devices) - except Exception as e: - raise exceptions.NotFound( - _(u"Can retrieve bundles for {entity}: {reason}" .format( - entity=entity_bare_jid.full(), reason=e))) - else: - cache[entity_bare_jid] = bundles - bundles = {entity_bare_jid: bundles} - - own_jid = client.jid.userhostJID() - if entity_bare_jid != own_jid: - # message will be copied to our devices, so we need to add our own bundles - bundles[own_jid] = cache[own_jid] - try: # first try may fail, in case of e.g. trust issue or missing bundle encrypted = yield omemo_session.encryptMessage( entity_bare_jid, - message, - bundles) + message) except omemo_excpt.EncryptionProblemsException as e: # we know the problem to solve, we can try to fix them - expect_problems = yield self.handleProblems(client, entity_bare_jid, bundles, e.problems) + bundles = {} + expect_problems = yield self.handleProblems(client, entity_bare_jid, bundles, + e.problems) # and try an encryption again. try: encrypted = yield omemo_session.encryptMessage( @@ -932,6 +917,10 @@ # we have an encrypted message let's decrypt it from_jid = jid.JID(message_elt['from']) + if from_jid.userhostJID() == client.jid.userhostJID(): + feedback_jid = jid.JID(message_elt['to']) + else: + feedback_jid = from_jid try: omemo_session = client._xep_0384_session except AttributeError: @@ -965,6 +954,11 @@ device_id=device_id, fingerprint=omemo_session.public_bundle.ik.encode('hex'), xml=encrypted_elt.toXml())) + user_msg = (D_(u"An OMEMO message from {sender} has not been encrypted for " + u"our device, we can't decrypt it").format( + sender=from_jid.full())) + extra = {C.MESS_EXTRA_INFO: C.EXTRA_INFO_DECR_ERR} + client.feedback(feedback_jid, user_msg, extra) defer.returnValue(False) except ValueError as e: log.warning(_(u"Invalid recipient ID: {msg}".format(msg=e))) @@ -1000,13 +994,14 @@ user_msg = (D_(u"An OMEMO message from {sender} can't be decrypted: {reason}") .format(sender=from_jid.full(), reason=e)) extra = {C.MESS_EXTRA_INFO: C.EXTRA_INFO_DECR_ERR} - client.feedback(from_jid, user_msg, extra) + client.feedback(feedback_jid, user_msg, extra) defer.returnValue(False) - if omemo_session.republish_bundle: - # we don't wait for the Deferred (i.e. no yield) on purpose - # there is no need to block the whole message workflow while - # updating the bundle - self.setBundle(client, omemo_session.public_bundle, device_id) + finally: + if omemo_session.republish_bundle: + # we don't wait for the Deferred (i.e. no yield) on purpose + # there is no need to block the whole message workflow while + # updating the bundle + self.setBundle(client, omemo_session.public_bundle, device_id) message_elt.children.remove(encrypted_elt) if plaintext: diff -r 114cdde9ff96 -r 0ab62dd3cf05 setup.py --- a/setup.py Thu Feb 28 18:57:06 2019 +0100 +++ b/setup.py Thu Feb 28 18:57:26 2019 +0100 @@ -50,7 +50,7 @@ 'urwid >= 1.2.0', 'urwid-satext >= 0.6.1', 'wokkel >= 0.7.1', - 'omemo >= 0.10.3', + 'omemo == 0.10.3', # FIXME: we block to this version until bĂȘta 'omemo_backend_signal', ]