| ... |
... |
@@ -76,6 +76,7 @@ |
|
76
|
76
|
from gajim.common.modules.util import prepare_stanza
|
|
77
|
77
|
from gajim.common.storage.omemo import OMEMOStorage
|
|
78
|
78
|
from gajim.common.structs import OutgoingMessage
|
|
|
79
|
+from gajim.common.util.decorators import lru_cache_with_ttl
|
|
79
|
80
|
|
|
80
|
81
|
ALLOWED_TAGS = [
|
|
81
|
82
|
('request', Namespace.RECEIPTS),
|
| ... |
... |
@@ -149,13 +150,9 @@ def __init__(self, client: types.Client) -> None: |
|
149
|
150
|
|
|
150
|
151
|
self._omemo_groupchats: set[str] = set()
|
|
151
|
152
|
self._muc_temp_store: dict[bytes, str] = {}
|
|
152
|
|
- self._query_for_bundles: list[str] = []
|
|
153
|
|
- self._device_bundle_querys: list[int] = []
|
|
154
|
|
- self._query_for_devicelists: list[str] = []
|
|
155
|
153
|
|
|
156
|
154
|
def _on_signed_in(self, _event: SignedIn) -> None:
|
|
157
|
|
- self._log.info('Announce Support after Sign In')
|
|
158
|
|
- self._query_for_bundles = []
|
|
|
155
|
+ self._log.info('Publish our bundle after sign in')
|
|
159
|
156
|
self.set_bundle()
|
|
160
|
157
|
self.request_devicelist()
|
|
161
|
158
|
|
| ... |
... |
@@ -180,8 +177,8 @@ def _on_room_joined(self, |
|
180
|
177
|
|
|
181
|
178
|
jid = str(contact.jid)
|
|
182
|
179
|
self._check_if_omemo_capable(jid)
|
|
183
|
|
- if self.is_omemo_groupchat(jid):
|
|
184
|
|
- self.get_affiliation_list(jid)
|
|
|
180
|
+ if self._is_omemo_groupchat(jid):
|
|
|
181
|
+ self._get_affiliation_list(jid)
|
|
185
|
182
|
|
|
186
|
183
|
def _on_republish_bundle(self,
|
|
187
|
184
|
_session: OMEMOSessionManager,
|
| ... |
... |
@@ -198,52 +195,79 @@ def backend(self) -> OMEMOSessionManager: |
|
198
|
195
|
def check_send_preconditions(self, contact: types.ChatContactT) -> bool:
|
|
199
|
196
|
jid = str(contact.jid)
|
|
200
|
197
|
if contact.is_groupchat:
|
|
201
|
|
- if not self.is_omemo_groupchat(jid):
|
|
202
|
|
- app.ged.raise_event(EncryptionInfo(
|
|
203
|
|
- account=contact.account,
|
|
204
|
|
- jid=contact.jid,
|
|
205
|
|
- message=EncryptionInfoMsg.BAD_OMEMO_CONFIG))
|
|
|
198
|
+ if not self._groupchat_pre_conditions_satisfied(contact):
|
|
206
|
199
|
return False
|
|
207
|
200
|
|
|
208
|
|
- missing = True
|
|
209
|
|
- for member_jid in self.backend.get_group_members(jid):
|
|
210
|
|
- if not self.are_keys_missing(member_jid):
|
|
211
|
|
- missing = False
|
|
212
|
|
- if missing:
|
|
213
|
|
- self._log.info('%s => No Trusted Fingerprints for %s',
|
|
214
|
|
- contact.account, jid)
|
|
215
|
|
- app.ged.raise_event(EncryptionInfo(
|
|
216
|
|
- account=contact.account,
|
|
217
|
|
- jid=contact.jid,
|
|
218
|
|
- message=EncryptionInfoMsg.NO_FINGERPRINTS))
|
|
219
|
|
- return False
|
|
220
|
201
|
else:
|
|
221
|
|
- # check if we have devices for the contact
|
|
222
|
|
- if not self.backend.get_devices(jid, without_self=True):
|
|
223
|
|
- self.request_devicelist(jid)
|
|
224
|
|
- app.ged.raise_event(EncryptionInfo(
|
|
225
|
|
- account=contact.account,
|
|
226
|
|
- jid=contact.jid,
|
|
227
|
|
- message=EncryptionInfoMsg.QUERY_DEVICES))
|
|
|
202
|
+ if not self._chat_pre_conditions_satisfied(contact):
|
|
228
|
203
|
return False
|
|
229
|
204
|
|
|
230
|
205
|
if self.backend.get_identity_infos(jid,
|
|
231
|
206
|
only_active=True,
|
|
232
|
207
|
trust=OMEMOTrust.UNDECIDED):
|
|
233
|
|
- self._log.info('%s => Undecided Fingerprints for %s',
|
|
234
|
|
- contact.account, jid)
|
|
|
208
|
+ self._log.info('Undecided keys for %s', jid)
|
|
235
|
209
|
app.ged.raise_event(EncryptionInfo(
|
|
236
|
210
|
account=contact.account,
|
|
237
|
211
|
jid=contact.jid,
|
|
238
|
212
|
message=EncryptionInfoMsg.UNDECIDED_FINGERPRINTS))
|
|
239
|
213
|
return False
|
|
240
|
214
|
|
|
241
|
|
- self._log.debug('%s => Sending Message to %s',
|
|
242
|
|
- contact.account, jid)
|
|
|
215
|
+ self._log.debug('Sending message to %s', jid)
|
|
243
|
216
|
|
|
244
|
217
|
return True
|
|
245
|
218
|
|
|
246
|
|
- def is_omemo_groupchat(self, room_jid: str) -> bool:
|
|
|
219
|
+ def _groupchat_pre_conditions_satisfied(
|
|
|
220
|
+ self,
|
|
|
221
|
+ contact: types.GroupchatContactT
|
|
|
222
|
+ ) -> bool:
|
|
|
223
|
+
|
|
|
224
|
+ jid = str(contact.jid)
|
|
|
225
|
+ if not self._is_omemo_groupchat(jid):
|
|
|
226
|
+ app.ged.raise_event(EncryptionInfo(
|
|
|
227
|
+ account=contact.account,
|
|
|
228
|
+ jid=contact.jid,
|
|
|
229
|
+ message=EncryptionInfoMsg.BAD_OMEMO_CONFIG))
|
|
|
230
|
+ return False
|
|
|
231
|
+
|
|
|
232
|
+ has_trusted_keys = False
|
|
|
233
|
+ for member_jid in self.backend.get_group_members(jid):
|
|
|
234
|
+ self._request_bundles_for_new_devices(member_jid)
|
|
|
235
|
+ if self._has_trusted_keys(member_jid):
|
|
|
236
|
+ has_trusted_keys = True
|
|
|
237
|
+
|
|
|
238
|
+ if not has_trusted_keys:
|
|
|
239
|
+ self._log.info('No trusted keys for %s', jid)
|
|
|
240
|
+ app.ged.raise_event(EncryptionInfo(
|
|
|
241
|
+ account=contact.account,
|
|
|
242
|
+ jid=contact.jid,
|
|
|
243
|
+ message=EncryptionInfoMsg.NO_FINGERPRINTS))
|
|
|
244
|
+ return False
|
|
|
245
|
+ return True
|
|
|
246
|
+
|
|
|
247
|
+ def _chat_pre_conditions_satisfied(
|
|
|
248
|
+ self,
|
|
|
249
|
+ contact: types.ChatContactT
|
|
|
250
|
+ ) -> bool:
|
|
|
251
|
+
|
|
|
252
|
+ jid = str(contact.jid)
|
|
|
253
|
+ if not self.backend.get_devices(jid, without_self=True):
|
|
|
254
|
+ self.request_devicelist(jid)
|
|
|
255
|
+ app.ged.raise_event(EncryptionInfo(
|
|
|
256
|
+ account=contact.account,
|
|
|
257
|
+ jid=contact.jid,
|
|
|
258
|
+ message=EncryptionInfoMsg.QUERY_DEVICES))
|
|
|
259
|
+ return False
|
|
|
260
|
+
|
|
|
261
|
+ if not self._has_trusted_keys(jid):
|
|
|
262
|
+ self._log.info('No trusted keys for %s', jid)
|
|
|
263
|
+ app.ged.raise_event(EncryptionInfo(
|
|
|
264
|
+ account=contact.account,
|
|
|
265
|
+ jid=contact.jid,
|
|
|
266
|
+ message=EncryptionInfoMsg.NO_FINGERPRINTS))
|
|
|
267
|
+ return False
|
|
|
268
|
+ return True
|
|
|
269
|
+
|
|
|
270
|
+ def _is_omemo_groupchat(self, room_jid: str) -> bool:
|
|
247
|
271
|
return room_jid in self._omemo_groupchats
|
|
248
|
272
|
|
|
249
|
273
|
def encrypt_message(self, event: OutgoingMessage) -> bool:
|
| ... |
... |
@@ -311,7 +335,7 @@ def _send_key_transport_message(self, |
|
311
|
335
|
return
|
|
312
|
336
|
|
|
313
|
337
|
transport_message = get_key_transport_message(typ, jid, omemo_message)
|
|
314
|
|
- self._log.info('Send key transport message %s (%s)', jid, devices)
|
|
|
338
|
+ self._log.info('Send key transport message to %s (%s)', jid, devices)
|
|
315
|
339
|
self._client.send_stanza(transport_message)
|
|
316
|
340
|
|
|
317
|
341
|
def _message_received(self,
|
| ... |
... |
@@ -352,7 +376,7 @@ def _message_received(self, |
|
352
|
376
|
raise NodeProcessed
|
|
353
|
377
|
|
|
354
|
378
|
if properties.omemo.payload not in self._muc_temp_store:
|
|
355
|
|
- self._log.warning("Can't decrypt own GroupChat Message")
|
|
|
379
|
+ self._log.warning("Can't decrypt own group chat message")
|
|
356
|
380
|
return
|
|
357
|
381
|
|
|
358
|
382
|
plaintext = self._muc_temp_store[properties.omemo.payload]
|
| ... |
... |
@@ -397,7 +421,7 @@ def _process_muc_message(self, |
|
397
|
421
|
return contact.real_jid.bare
|
|
398
|
422
|
|
|
399
|
423
|
self._log.error(
|
|
400
|
|
- 'Unable to find JID for group chat message from %s', resource)
|
|
|
424
|
+ 'Unable to find jid for group chat message from %s', resource)
|
|
401
|
425
|
return None
|
|
402
|
426
|
|
|
403
|
427
|
def _process_mam_message(self,
|
| ... |
... |
@@ -408,7 +432,7 @@ def _process_mam_message(self, |
|
408
|
432
|
if properties.from_muc:
|
|
409
|
433
|
self._log.info('MUC MAM Message received')
|
|
410
|
434
|
if properties.muc_user is None or properties.muc_user.jid is None:
|
|
411
|
|
- self._log.warning('Received MAM Message which can '
|
|
|
435
|
+ self._log.warning('Received MAM message which can '
|
|
412
|
436
|
'not be mapped to a real jid')
|
|
413
|
437
|
return None
|
|
414
|
438
|
return properties.muc_user.jid.bare
|
| ... |
... |
@@ -439,13 +463,13 @@ def _on_muc_user_presence(self, |
|
439
|
463
|
else:
|
|
440
|
464
|
self.backend.add_group_member(room, jid)
|
|
441
|
465
|
|
|
442
|
|
- if self.is_omemo_groupchat(room):
|
|
443
|
|
- if not self.is_contact_in_roster(jid):
|
|
|
466
|
+ if self._is_omemo_groupchat(room):
|
|
|
467
|
+ if not self._is_contact_in_roster(jid):
|
|
444
|
468
|
# Query Devicelists from JIDs not in our Roster
|
|
445
|
|
- self._log.info('%s not in Roster, query devicelist...', jid)
|
|
446
|
|
- self.request_devicelist(jid)
|
|
|
469
|
+ self._log.info('%s not in roster, query devicelist...', jid)
|
|
|
470
|
+ self._request_device_list_ttl(jid)
|
|
447
|
471
|
|
|
448
|
|
- def get_affiliation_list(self, room_jid: str) -> None:
|
|
|
472
|
+ def _get_affiliation_list(self, room_jid: str) -> None:
|
|
449
|
473
|
for affiliation in ('owner', 'admin', 'member'):
|
|
450
|
474
|
self._nbxmpp('MUC').get_affiliation(
|
|
451
|
475
|
room_jid,
|
| ... |
... |
@@ -465,12 +489,11 @@ def _on_affiliations_received(self, task: Task) -> None: |
|
465
|
489
|
jid = str(user_jid)
|
|
466
|
490
|
self.backend.add_group_member(room_jid, jid)
|
|
467
|
491
|
|
|
468
|
|
- if not self.is_contact_in_roster(jid):
|
|
469
|
|
- # Query Devicelists from JIDs not in our Roster
|
|
470
|
|
- self._log.info('%s not in Roster, query devicelist...', jid)
|
|
471
|
|
- self.request_devicelist(jid)
|
|
|
492
|
+ if not self._is_contact_in_roster(jid):
|
|
|
493
|
+ self._log.info('%s not in roster, query devicelist...', jid)
|
|
|
494
|
+ self._request_device_list_ttl(jid)
|
|
472
|
495
|
|
|
473
|
|
- def is_contact_in_roster(self, jid: str) -> bool:
|
|
|
496
|
+ def _is_contact_in_roster(self, jid: str) -> bool:
|
|
474
|
497
|
if jid == self._own_jid:
|
|
475
|
498
|
return True
|
|
476
|
499
|
|
| ... |
... |
@@ -490,60 +513,19 @@ def _check_if_omemo_capable(self, jid: str) -> None: |
|
490
|
513
|
self._log.info('OMEMO room removed due to config change: %s', jid)
|
|
491
|
514
|
self._omemo_groupchats.discard(jid)
|
|
492
|
515
|
|
|
493
|
|
- def _check_for_missing_sessions(self, jid: str) -> None:
|
|
494
|
|
- devices_without_session = self.backend.get_devices_without_sessions(jid)
|
|
495
|
|
- for device_id in devices_without_session:
|
|
496
|
|
- if device_id in self._device_bundle_querys:
|
|
497
|
|
- continue
|
|
498
|
|
- self._device_bundle_querys.append(device_id)
|
|
499
|
|
- self.request_bundle(jid, device_id)
|
|
500
|
|
-
|
|
501
|
|
- def are_keys_missing(self, contact_jid: str) -> bool:
|
|
502
|
|
- ''' Checks if devicekeys are missing and queries the
|
|
503
|
|
- bundles
|
|
504
|
|
-
|
|
505
|
|
- Parameters
|
|
506
|
|
- ----------
|
|
507
|
|
- contact_jid : str
|
|
508
|
|
- bare jid of the contact
|
|
509
|
|
-
|
|
510
|
|
- Returns
|
|
511
|
|
- -------
|
|
512
|
|
- bool
|
|
513
|
|
- Returns True if there are no trusted Fingerprints
|
|
514
|
|
- '''
|
|
515
|
|
-
|
|
516
|
|
- # Fetch Bundles of own other Devices
|
|
517
|
|
- if self._own_jid not in self._query_for_bundles:
|
|
518
|
|
-
|
|
519
|
|
- devices_without_session = self.backend.get_devices_without_sessions(
|
|
520
|
|
- self._own_jid)
|
|
521
|
|
-
|
|
522
|
|
- self._query_for_bundles.append(self._own_jid)
|
|
523
|
|
-
|
|
524
|
|
- if devices_without_session:
|
|
525
|
|
- for device_id in devices_without_session:
|
|
526
|
|
- self.request_bundle(self._own_jid, device_id)
|
|
527
|
|
-
|
|
528
|
|
- # Fetch Bundles of contacts devices
|
|
529
|
|
- if contact_jid not in self._query_for_bundles:
|
|
530
|
|
-
|
|
531
|
|
- devices_without_session = self.backend.get_devices_without_sessions(
|
|
532
|
|
- contact_jid)
|
|
533
|
|
-
|
|
534
|
|
- self._query_for_bundles.append(contact_jid)
|
|
535
|
|
-
|
|
536
|
|
- if devices_without_session:
|
|
537
|
|
- for device_id in devices_without_session:
|
|
538
|
|
- self.request_bundle(contact_jid, device_id)
|
|
|
516
|
+ def _request_bundles_for_new_devices(self, jid_: str) -> None:
|
|
|
517
|
+ for jid in [jid_, self._own_jid]:
|
|
|
518
|
+ device_ids = self.backend.get_devices_without_sessions(jid)
|
|
|
519
|
+ for device_id in device_ids:
|
|
|
520
|
+ self._request_bundle_ttl(jid, device_id)
|
|
539
|
521
|
|
|
|
522
|
+ def _has_trusted_keys(self, jid: str) -> bool:
|
|
540
|
523
|
if self.backend.get_identity_infos(
|
|
541
|
|
- contact_jid,
|
|
|
524
|
+ jid,
|
|
542
|
525
|
only_active=True,
|
|
543
|
526
|
trust=[OMEMOTrust.VERIFIED, OMEMOTrust.BLIND]):
|
|
544
|
|
- return False
|
|
545
|
|
-
|
|
546
|
|
- return True
|
|
|
527
|
+ return True
|
|
|
528
|
+ return False
|
|
547
|
529
|
|
|
548
|
530
|
def set_bundle(self, bundle: OMEMOBundle | None = None) -> None:
|
|
549
|
531
|
if bundle is None:
|
| ... |
... |
@@ -551,11 +533,15 @@ def set_bundle(self, bundle: OMEMOBundle | None = None) -> None: |
|
551
|
533
|
self._nbxmpp('OMEMO').set_bundle(bundle,
|
|
552
|
534
|
self.backend.get_our_device())
|
|
553
|
535
|
|
|
|
536
|
+ @lru_cache_with_ttl(maxsize=512, ttl=7200)
|
|
|
537
|
+ def _request_bundle_ttl(self, jid: str, device_id: int) -> None:
|
|
|
538
|
+ self.request_bundle(jid, device_id)
|
|
|
539
|
+
|
|
554
|
540
|
@as_task
|
|
555
|
541
|
def request_bundle(self, jid: str, device_id: int):
|
|
556
|
542
|
_task = yield # noqa: F841
|
|
557
|
543
|
|
|
558
|
|
- self._log.info('Fetch device bundle %s %s', device_id, jid)
|
|
|
544
|
+ self._log.info('Request device bundle %s %s', device_id, jid)
|
|
559
|
545
|
|
|
560
|
546
|
bundle = yield self._nbxmpp('OMEMO').request_bundle(jid, device_id)
|
|
561
|
547
|
|
| ... |
... |
@@ -588,6 +574,10 @@ def clear_devicelist(self) -> None: |
|
588
|
574
|
self._own_jid, [self.backend.get_our_device()])
|
|
589
|
575
|
self.set_devicelist()
|
|
590
|
576
|
|
|
|
577
|
+ @lru_cache_with_ttl(maxsize=512, ttl=7200)
|
|
|
578
|
+ def _request_device_list_ttl(self, jid: str) -> None:
|
|
|
579
|
+ self.request_devicelist(jid)
|
|
|
580
|
+
|
|
591
|
581
|
@as_task
|
|
592
|
582
|
def request_devicelist(self, jid: Optional[str] = None):
|
|
593
|
583
|
_task = yield # noqa: F841
|
| ... |
... |
@@ -595,10 +585,7 @@ def request_devicelist(self, jid: Optional[str] = None): |
|
595
|
585
|
if jid is None:
|
|
596
|
586
|
jid = self._own_jid
|
|
597
|
587
|
|
|
598
|
|
- if jid in self._query_for_devicelists:
|
|
599
|
|
- return
|
|
600
|
|
-
|
|
601
|
|
- self._query_for_devicelists.append(jid)
|
|
|
588
|
+ self._log.info('Request devicelist for %s', jid)
|
|
602
|
589
|
|
|
603
|
590
|
devicelist = yield self._nbxmpp('OMEMO').request_devicelist(jid=jid)
|
|
604
|
591
|
if is_error(devicelist) or devicelist is None:
|
| ... |
... |
@@ -634,16 +621,13 @@ def _process_devicelist_update(self, |
|
634
|
621
|
# Pass a copy, we need the full list for potential set_devicelist()
|
|
635
|
622
|
self.backend.update_devicelist(jid, list(devicelist))
|
|
636
|
623
|
|
|
637
|
|
- if jid in self._query_for_devicelists:
|
|
638
|
|
- self._query_for_devicelists.remove(jid)
|
|
639
|
|
-
|
|
640
|
624
|
if own_devices:
|
|
641
|
625
|
if not self.backend.is_our_device_published():
|
|
642
|
626
|
# Our own device_id is not in the list, it could be
|
|
643
|
627
|
# overwritten by some other client
|
|
644
|
628
|
self.set_devicelist(devicelist)
|
|
645
|
629
|
|
|
646
|
|
- self._check_for_missing_sessions(jid)
|
|
|
630
|
+ self._request_bundles_for_new_devices(jid)
|
|
647
|
631
|
|
|
648
|
632
|
def _debug_print_stanza(self, stanza: Any) -> None:
|
|
649
|
633
|
stanzastr = '\n' + stanza.__str__(fancy=True)
|