Mercurial > libervia-backend
comparison sat/plugins/plugin_xep_0384.py @ 2662:0bef44f8e8ca
plugin XEP-0384: PEP handling + mark as encrypted:
devices are gotten using PEP and +notify, and bundles are then retrieved, and kept in cache.
This allow to remove the test code which was always requesting devices and bundles before
sending a message. For now OMEMO plugin will only work with people in roster, this will be
fixed in the future.
author | Goffi <goffi@goffi.org> |
---|---|
date | Sat, 11 Aug 2018 18:24:55 +0200 |
parents | e7bfbded652a |
children | eb58f26ed236 |
comparison
equal
deleted
inserted
replaced
2661:661f66d41215 | 2662:0bef44f8e8ca |
---|---|
45 PLUGIN_INFO = { | 45 PLUGIN_INFO = { |
46 C.PI_NAME: u"OMEMO", | 46 C.PI_NAME: u"OMEMO", |
47 C.PI_IMPORT_NAME: u"OMEMO", | 47 C.PI_IMPORT_NAME: u"OMEMO", |
48 C.PI_TYPE: u"SEC", | 48 C.PI_TYPE: u"SEC", |
49 C.PI_PROTOCOLS: [u"XEP-0384"], | 49 C.PI_PROTOCOLS: [u"XEP-0384"], |
50 C.PI_DEPENDENCIES: [u"XEP-0280", u"XEP-0334", u"XEP-0060"], | 50 C.PI_DEPENDENCIES: [u"XEP-0163", u"XEP-0280", u"XEP-0334", u"XEP-0060"], |
51 C.PI_MAIN: u"OMEMO", | 51 C.PI_MAIN: u"OMEMO", |
52 C.PI_HANDLER: u"no", | 52 C.PI_HANDLER: u"no", |
53 C.PI_DESCRIPTION: _(u"""Implementation of OMEMO"""), | 53 C.PI_DESCRIPTION: _(u"""Implementation of OMEMO"""), |
54 } | 54 } |
55 | 55 |
264 self._p_carbons = host.plugins[u"XEP-0280"] | 264 self._p_carbons = host.plugins[u"XEP-0280"] |
265 self._p = host.plugins[u"XEP-0060"] | 265 self._p = host.plugins[u"XEP-0060"] |
266 host.trigger.add("MessageReceived", self._messageReceivedTrigger, priority=100050) | 266 host.trigger.add("MessageReceived", self._messageReceivedTrigger, priority=100050) |
267 host.trigger.add("sendMessageData", self._sendMessageDataTrigger) | 267 host.trigger.add("sendMessageData", self._sendMessageDataTrigger) |
268 self.host.registerEncryptionPlugin(self, u"OMEMO", NS_OMEMO, 100) | 268 self.host.registerEncryptionPlugin(self, u"OMEMO", NS_OMEMO, 100) |
269 pep = host.plugins['XEP-0163'] | |
270 pep.addPEPEvent("OMEMO_DEVICES", NS_OMEMO_DEVICES, self.onNewDevices) | |
269 | 271 |
270 @defer.inlineCallbacks | 272 @defer.inlineCallbacks |
271 def profileConnected(self, client): | 273 def profileConnected(self, client): |
272 # we first need to get devices ids (including our own) | 274 # we first need to get devices ids (including our own) |
273 persistent_dict = persistent.LazyPersistentBinaryDict("XEP-0384", client.profile) | 275 persistent_dict = persistent.LazyPersistentBinaryDict("XEP-0384", client.profile) |
288 devices.add(device_id) | 290 devices.add(device_id) |
289 yield self.setDevices(client, devices) | 291 yield self.setDevices(client, devices) |
290 | 292 |
291 omemo_storage = OmemoStorage(client, device_id, persistent_dict) | 293 omemo_storage = OmemoStorage(client, device_id, persistent_dict) |
292 omemo_session = yield OmemoSession.create(client, omemo_storage, device_id) | 294 omemo_session = yield OmemoSession.create(client, omemo_storage, device_id) |
295 client._xep_0384_cache = {} | |
293 client._xep_0384_session = omemo_session | 296 client._xep_0384_session = omemo_session |
294 client._xep_0384_device_id = device_id | 297 client._xep_0384_device_id = device_id |
295 yield omemo_session.newDeviceList(devices, client.jid) | 298 yield omemo_session.newDeviceList(devices, client.jid) |
296 if omemo_session.state.changed: | 299 if omemo_session.state.changed: |
297 log.info(_(u"Saving public bundle for this device ({device_id})").format( | 300 log.info(_(u"Saving public bundle for this device ({device_id})").format( |
301 | 304 |
302 ## XMPP PEP nodes manipulation | 305 ## XMPP PEP nodes manipulation |
303 | 306 |
304 # devices | 307 # devices |
305 | 308 |
306 @defer.inlineCallbacks | 309 def parseDevices(self, items): |
307 def getDevices(self, client, entity_jid=None): | 310 """Parse devices found in items |
308 """Retrieve list of registered OMEMO devices | 311 |
309 | 312 @param items(iterable[domish.Element]): items as retrieved by getItems |
310 @param entity_jid(jid.JID, None): get devices from this entity | 313 @return set[int]: parsed devices |
311 None to get our own devices | 314 """ |
312 @return (set(int)): list of devices | |
313 """ | |
314 if entity_jid is not None: | |
315 assert not entity_jid.resource | |
316 devices = set() | 315 devices = set() |
317 try: | |
318 items, metadata = yield self._p.getItems(client, entity_jid, NS_OMEMO_DEVICES) | |
319 except error.StanzaError as e: | |
320 if e.condition == 'item-not-found': | |
321 log.info(_(u"there is no node to handle OMEMO devices")) | |
322 defer.returnValue(devices) | |
323 | |
324 if len(items) > 1: | 316 if len(items) > 1: |
325 log.warning(_(u"OMEMO devices list is stored in more that one items, " | 317 log.warning(_(u"OMEMO devices list is stored in more that one items, " |
326 u"this is not expected")) | 318 u"this is not expected")) |
327 if items: | 319 if items: |
328 try: | 320 try: |
339 except ValueError: | 331 except ValueError: |
340 log.warning(_(u'invalid device id: {device_id}').format( | 332 log.warning(_(u'invalid device id: {device_id}').format( |
341 device_id=device_elt['id'])) | 333 device_id=device_elt['id'])) |
342 else: | 334 else: |
343 devices.add(device_id) | 335 devices.add(device_id) |
336 return devices | |
337 | |
338 @defer.inlineCallbacks | |
339 def getDevices(self, client, entity_jid=None): | |
340 """Retrieve list of registered OMEMO devices | |
341 | |
342 @param entity_jid(jid.JID, None): get devices from this entity | |
343 None to get our own devices | |
344 @return (set(int)): list of devices | |
345 """ | |
346 if entity_jid is not None: | |
347 assert not entity_jid.resource | |
348 try: | |
349 items, metadata = yield self._p.getItems(client, entity_jid, NS_OMEMO_DEVICES) | |
350 except error.StanzaError as e: | |
351 if e.condition == 'item-not-found': | |
352 log.info(_(u"there is no node to handle OMEMO devices")) | |
353 defer.returnValue(set()) | |
354 | |
355 devices = self.parseDevices(items) | |
344 defer.returnValue(devices) | 356 defer.returnValue(devices) |
345 | 357 |
346 def setDevicesEb(self, failure_): | 358 def setDevicesEb(self, failure_): |
347 log.warning(_(u"Can't set devices: {reason}").format(reason=failure_)) | 359 log.warning(_(u"Can't set devices: {reason}").format(reason=failure_)) |
348 | 360 |
464 node = NS_OMEMO_BUNDLE.format(device_id=device_id) | 476 node = NS_OMEMO_BUNDLE.format(device_id=device_id) |
465 d = self._p.sendItem(client, None, node, bundle_elt, item_id=self._p.ID_SINGLETON) | 477 d = self._p.sendItem(client, None, node, bundle_elt, item_id=self._p.ID_SINGLETON) |
466 d.addErrback(self.setBundleEb) | 478 d.addErrback(self.setBundleEb) |
467 return d | 479 return d |
468 | 480 |
481 ## PEP node events callbacks | |
482 | |
483 @defer.inlineCallbacks | |
484 def onNewDevices(self, itemsEvent, profile): | |
485 client = self.host.getClient(profile) | |
486 cache = client._xep_0384_cache | |
487 omemo_session = client._xep_0384_session | |
488 entity = itemsEvent.sender | |
489 entity_cache = cache.setdefault(entity, {}) | |
490 devices = self.parseDevices(itemsEvent.items) | |
491 omemo_session.newDeviceList(devices, entity) | |
492 missing_devices = devices.difference(entity_cache.keys()) | |
493 if missing_devices: | |
494 missing_bundles = yield self.getBundles(client, entity, missing_devices) | |
495 entity_cache.update(missing_bundles) | |
496 | |
469 ## triggers | 497 ## triggers |
470 | 498 |
471 @defer.inlineCallbacks | 499 @defer.inlineCallbacks |
472 def encryptMessage(self, client, entity_bare_jid, message): | 500 def encryptMessage(self, client, entity_bare_jid, message): |
473 omemo_session = client._xep_0384_session | 501 omemo_session = client._xep_0384_session |
474 devices = yield self.getDevices(client, entity_bare_jid) | 502 try: |
475 omemo_session.newDeviceList(devices, entity_bare_jid) | 503 bundles = client._xep_0384_cache[entity_bare_jid] |
476 bundles = yield self.getBundles(client, entity_bare_jid, devices) | 504 except KeyError: |
505 raise exceptions.NotFound(_(u"No OMEMO encryption information found for this" | |
506 u"contact ({entity})".format( | |
507 entity=entity_bare_jid.full()))) | |
477 encrypted = yield omemo_session.encryptMessage( | 508 encrypted = yield omemo_session.encryptMessage( |
478 entity_bare_jid, | 509 entity_bare_jid, |
479 message, | 510 message, |
480 {entity_bare_jid: bundles}) | 511 {entity_bare_jid: bundles}) |
481 defer.returnValue(encrypted) | 512 defer.returnValue(encrypted) |
545 self.setBundle(client, bundle, device_id) | 576 self.setBundle(client, bundle, device_id) |
546 | 577 |
547 message_elt.children.remove(encrypted_elt) | 578 message_elt.children.remove(encrypted_elt) |
548 if plaintext: | 579 if plaintext: |
549 message_elt.addElement("body", content=plaintext.decode('utf-8')) | 580 message_elt.addElement("body", content=plaintext.decode('utf-8')) |
581 post_treat.addCallback(client.encryption.markAsEncrypted) | |
550 defer.returnValue(True) | 582 defer.returnValue(True) |
551 | 583 |
552 @defer.inlineCallbacks | 584 @defer.inlineCallbacks |
553 def _sendMessageDataTrigger(self, client, mess_data): | 585 def _sendMessageDataTrigger(self, client, mess_data): |
554 encryption = mess_data.get(C.MESS_KEY_ENCRYPTION) | 586 encryption = mess_data.get(C.MESS_KEY_ENCRYPTION) |