slidge 0.1.2__py3-none-any.whl → 0.2.0a0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- slidge/__init__.py +3 -5
- slidge/__main__.py +2 -196
- slidge/__version__.py +5 -0
- slidge/command/adhoc.py +8 -1
- slidge/command/admin.py +5 -6
- slidge/command/base.py +1 -2
- slidge/command/register.py +32 -16
- slidge/command/user.py +85 -5
- slidge/contact/contact.py +93 -31
- slidge/contact/roster.py +54 -39
- slidge/core/config.py +13 -7
- slidge/core/gateway/base.py +139 -34
- slidge/core/gateway/disco.py +2 -4
- slidge/core/gateway/mam.py +1 -4
- slidge/core/gateway/ping.py +2 -3
- slidge/core/gateway/presence.py +1 -1
- slidge/core/gateway/registration.py +32 -21
- slidge/core/gateway/search.py +3 -5
- slidge/core/gateway/session_dispatcher.py +109 -51
- slidge/core/gateway/vcard_temp.py +6 -4
- slidge/core/mixins/__init__.py +11 -1
- slidge/core/mixins/attachment.py +15 -10
- slidge/core/mixins/avatar.py +66 -18
- slidge/core/mixins/base.py +8 -2
- slidge/core/mixins/message.py +11 -7
- slidge/core/mixins/message_maker.py +17 -9
- slidge/core/mixins/presence.py +14 -4
- slidge/core/pubsub.py +54 -212
- slidge/core/session.py +65 -33
- slidge/db/__init__.py +4 -0
- slidge/db/alembic/env.py +64 -0
- slidge/db/alembic/script.py.mako +26 -0
- slidge/db/alembic/versions/09f27f098baa_add_missing_attributes_in_room.py +36 -0
- slidge/db/alembic/versions/29f5280c61aa_store_subject_setter_in_room.py +37 -0
- slidge/db/alembic/versions/8d2ced764698_rely_on_db_to_store_contacts_rooms_and_.py +133 -0
- slidge/db/alembic/versions/aa9d82a7f6ef_db_creation.py +76 -0
- slidge/db/alembic/versions/b33993e87db3_move_everything_to_persistent_db.py +214 -0
- slidge/db/alembic/versions/e91195719c2c_store_users_avatars_persistently.py +26 -0
- slidge/db/avatar.py +224 -0
- slidge/db/meta.py +65 -0
- slidge/db/models.py +365 -0
- slidge/db/store.py +976 -0
- slidge/group/archive.py +13 -14
- slidge/group/bookmarks.py +59 -56
- slidge/group/participant.py +81 -29
- slidge/group/room.py +242 -142
- slidge/main.py +201 -0
- slidge/migration.py +30 -0
- slidge/slixfix/__init__.py +35 -2
- slidge/slixfix/roster.py +11 -4
- slidge/slixfix/xep_0292/vcard4.py +1 -0
- slidge/util/db.py +1 -47
- slidge/util/test.py +21 -4
- slidge/util/types.py +24 -4
- {slidge-0.1.2.dist-info → slidge-0.2.0a0.dist-info}/METADATA +3 -1
- slidge-0.2.0a0.dist-info/RECORD +108 -0
- slidge/core/cache.py +0 -183
- slidge/util/schema.sql +0 -126
- slidge/util/sql.py +0 -508
- slidge-0.1.2.dist-info/RECORD +0 -96
- {slidge-0.1.2.dist-info → slidge-0.2.0a0.dist-info}/LICENSE +0 -0
- {slidge-0.1.2.dist-info → slidge-0.2.0a0.dist-info}/WHEEL +0 -0
- {slidge-0.1.2.dist-info → slidge-0.2.0a0.dist-info}/entry_points.txt +0 -0
slidge/core/gateway/base.py
CHANGED
@@ -8,7 +8,16 @@ import re
|
|
8
8
|
import tempfile
|
9
9
|
from copy import copy
|
10
10
|
from datetime import datetime
|
11
|
-
from typing import
|
11
|
+
from typing import (
|
12
|
+
TYPE_CHECKING,
|
13
|
+
Any,
|
14
|
+
Callable,
|
15
|
+
Collection,
|
16
|
+
Mapping,
|
17
|
+
Optional,
|
18
|
+
Sequence,
|
19
|
+
Union,
|
20
|
+
)
|
12
21
|
|
13
22
|
import aiohttp
|
14
23
|
import qrcode
|
@@ -24,11 +33,11 @@ from ...command.admin import Exec
|
|
24
33
|
from ...command.base import Command, FormField
|
25
34
|
from ...command.chat_command import ChatCommandProvider
|
26
35
|
from ...command.register import RegistrationType
|
36
|
+
from ...db import GatewayUser, SlidgeStore
|
37
|
+
from ...db.avatar import avatar_cache
|
27
38
|
from ...slixfix.roster import RosterBackend
|
28
39
|
from ...slixfix.xep_0292.vcard4 import VCard4Provider
|
29
40
|
from ...util import ABCSubclassableOnceAtMost
|
30
|
-
from ...util.db import GatewayUser, user_store
|
31
|
-
from ...util.sql import db
|
32
41
|
from ...util.types import AvatarType, MessageOrPresenceTypeVar
|
33
42
|
from .. import config
|
34
43
|
from ..mixins import MessageMixin
|
@@ -141,6 +150,23 @@ class BaseGateway(
|
|
141
150
|
)
|
142
151
|
REGISTRATION_QR_INSTRUCTIONS = "Flash this code or follow this link"
|
143
152
|
|
153
|
+
PREFERENCES = [
|
154
|
+
FormField(
|
155
|
+
var="sync_presence",
|
156
|
+
label="Propagate your XMPP presence to the legacy network.",
|
157
|
+
value="true",
|
158
|
+
required=True,
|
159
|
+
type="boolean",
|
160
|
+
),
|
161
|
+
FormField(
|
162
|
+
var="sync_avatar",
|
163
|
+
label="Propagate your XMPP avatar to the legacy network.",
|
164
|
+
value="true",
|
165
|
+
required=True,
|
166
|
+
type="boolean",
|
167
|
+
),
|
168
|
+
]
|
169
|
+
|
144
170
|
ROSTER_GROUP: str = "slidge"
|
145
171
|
"""
|
146
172
|
Name of the group assigned to a :class:`.LegacyContact` automagically
|
@@ -202,6 +228,47 @@ class BaseGateway(
|
|
202
228
|
mtype: MessageTypes = "chat"
|
203
229
|
is_group = False
|
204
230
|
_can_send_carbon = False
|
231
|
+
store: SlidgeStore
|
232
|
+
avatar_pk: int
|
233
|
+
|
234
|
+
AVATAR_ID_TYPE: Callable[[str], Any] = str
|
235
|
+
"""
|
236
|
+
Modify this if the legacy network uses unique avatar IDs that are not strings.
|
237
|
+
|
238
|
+
This is required because we store those IDs as TEXT in the persistent SQL DB.
|
239
|
+
The callable specified here will receive is responsible for converting the
|
240
|
+
serialised-as-text version of the avatar unique ID back to the proper type.
|
241
|
+
Common example: ``int``.
|
242
|
+
"""
|
243
|
+
# FIXME: do we really need this since we have session.xmpp_to_legacy_msg_id?
|
244
|
+
# (maybe we do)
|
245
|
+
LEGACY_MSG_ID_TYPE: Callable[[str], Any] = str
|
246
|
+
"""
|
247
|
+
Modify this if the legacy network uses unique message IDs that are not strings.
|
248
|
+
|
249
|
+
This is required because we store those IDs as TEXT in the persistent SQL DB.
|
250
|
+
The callable specified here will receive is responsible for converting the
|
251
|
+
serialised-as-text version of the message unique ID back to the proper type.
|
252
|
+
Common example: ``int``.
|
253
|
+
"""
|
254
|
+
LEGACY_CONTACT_ID_TYPE: Callable[[str], Any] = str
|
255
|
+
"""
|
256
|
+
Modify this if the legacy network uses unique contact IDs that are not strings.
|
257
|
+
|
258
|
+
This is required because we store those IDs as TEXT in the persistent SQL DB.
|
259
|
+
The callable specified here is responsible for converting the
|
260
|
+
serialised-as-text version of the contact unique ID back to the proper type.
|
261
|
+
Common example: ``int``.
|
262
|
+
"""
|
263
|
+
LEGACY_ROOM_ID_TYPE: Callable[[str], Any] = str
|
264
|
+
"""
|
265
|
+
Modify this if the legacy network uses unique room IDs that are not strings.
|
266
|
+
|
267
|
+
This is required because we store those IDs as TEXT in the persistent SQL DB.
|
268
|
+
The callable specified here is responsible for converting the
|
269
|
+
serialised-as-text version of the room unique ID back to the proper type.
|
270
|
+
Common example: ``int``.
|
271
|
+
"""
|
205
272
|
|
206
273
|
def __init__(self):
|
207
274
|
self.datetime_started = datetime.now()
|
@@ -222,7 +289,6 @@ class BaseGateway(
|
|
222
289
|
},
|
223
290
|
"xep_0100": {
|
224
291
|
"component_name": self.COMPONENT_NAME,
|
225
|
-
"user_store": user_store,
|
226
292
|
"type": self.COMPONENT_TYPE,
|
227
293
|
},
|
228
294
|
"xep_0184": {
|
@@ -241,11 +307,15 @@ class BaseGateway(
|
|
241
307
|
self.use_origin_id = False
|
242
308
|
|
243
309
|
self.jid_validator: re.Pattern = re.compile(config.USER_JID_VALIDATOR)
|
244
|
-
self.qr_pending_registrations = dict[str, asyncio.Future[
|
310
|
+
self.qr_pending_registrations = dict[str, asyncio.Future[Optional[dict]]]()
|
245
311
|
|
246
312
|
self.session_cls: BaseSession = BaseSession.get_unique_subclass()
|
247
313
|
self.session_cls.xmpp = self
|
248
314
|
|
315
|
+
from ...group.room import LegacyMUC
|
316
|
+
|
317
|
+
LegacyMUC.get_unique_subclass().xmpp = self
|
318
|
+
|
249
319
|
self.get_session_from_stanza: Callable[
|
250
320
|
[Union[Message, Presence, Iq]], BaseSession
|
251
321
|
] = self.session_cls.from_stanza # type: ignore
|
@@ -255,7 +325,7 @@ class BaseGateway(
|
|
255
325
|
|
256
326
|
self.register_plugins()
|
257
327
|
self.__register_slixmpp_events()
|
258
|
-
self.roster.set_backend(RosterBackend)
|
328
|
+
self.roster.set_backend(RosterBackend(self))
|
259
329
|
|
260
330
|
self.register_plugin("pubsub", {"component_name": self.COMPONENT_NAME})
|
261
331
|
self.pubsub: PubSubComponent = self["pubsub"]
|
@@ -294,7 +364,14 @@ class BaseGateway(
|
|
294
364
|
|
295
365
|
self.__register_commands()
|
296
366
|
|
297
|
-
|
367
|
+
self.__mam_cleanup_task = self.loop.create_task(self.__mam_cleanup())
|
368
|
+
|
369
|
+
async def __mam_cleanup(self):
|
370
|
+
if not config.MAM_MAX_DAYS:
|
371
|
+
return
|
372
|
+
while True:
|
373
|
+
await asyncio.sleep(3600 * 6)
|
374
|
+
self.store.mam.nuke_older_than(config.MAM_MAX_DAYS)
|
298
375
|
|
299
376
|
def __register_commands(self):
|
300
377
|
for cls in Command.subclasses:
|
@@ -303,7 +380,7 @@ class BaseGateway(
|
|
303
380
|
continue
|
304
381
|
if cls is Exec:
|
305
382
|
if config.DEV_MODE:
|
306
|
-
log.warning("/!\ DEV MODE ENABLED /!\\")
|
383
|
+
log.warning(r"/!\ DEV MODE ENABLED /!\\")
|
307
384
|
else:
|
308
385
|
continue
|
309
386
|
c = cls(self)
|
@@ -356,7 +433,7 @@ class BaseGateway(
|
|
356
433
|
mfrom = msg.get_from()
|
357
434
|
resource = mfrom.resource
|
358
435
|
try:
|
359
|
-
muc.
|
436
|
+
muc.remove_user_resource(resource)
|
360
437
|
except KeyError:
|
361
438
|
# this actually happens quite frequently on for both beagle and monal
|
362
439
|
# (not sure why?), but is of no consequence
|
@@ -374,11 +451,15 @@ class BaseGateway(
|
|
374
451
|
await disco.del_feature(feature="urn:xmpp:http:upload:0", jid=self.boundjid)
|
375
452
|
await self.plugin["xep_0115"].update_caps(jid=self.boundjid)
|
376
453
|
|
377
|
-
|
378
|
-
|
379
|
-
|
454
|
+
if self.COMPONENT_AVATAR:
|
455
|
+
cached_avatar = await avatar_cache.convert_or_get(
|
456
|
+
self.COMPONENT_AVATAR, None
|
457
|
+
)
|
458
|
+
self.avatar_pk = cached_avatar.pk
|
459
|
+
else:
|
460
|
+
cached_avatar = None
|
380
461
|
|
381
|
-
for user in
|
462
|
+
for user in self.store.users.get_all():
|
382
463
|
# TODO: before this, we should check if the user has removed us from their roster
|
383
464
|
# while we were offline and trigger unregister from there. Presence probe does not seem
|
384
465
|
# to work in this case, there must be another way. privileged entity could be used
|
@@ -396,10 +477,14 @@ class BaseGateway(
|
|
396
477
|
)
|
397
478
|
continue
|
398
479
|
self.send_presence(
|
399
|
-
pto=user.
|
480
|
+
pto=user.jid.bare, ptype="probe"
|
400
481
|
) # ensure we get all resources for user
|
401
482
|
session = self.session_cls.from_user(user)
|
402
483
|
session.create_task(self.__login_wrap(session))
|
484
|
+
if cached_avatar is not None:
|
485
|
+
await self.pubsub.broadcast_avatar(
|
486
|
+
self.boundjid.bare, session.user_jid, cached_avatar
|
487
|
+
)
|
403
488
|
|
404
489
|
log.info("Slidge has successfully started")
|
405
490
|
|
@@ -460,7 +545,7 @@ class BaseGateway(
|
|
460
545
|
try:
|
461
546
|
status = await session.login()
|
462
547
|
except Exception as e:
|
463
|
-
log.warning("Login problem for %s", session.
|
548
|
+
log.warning("Login problem for %s", session.user_jid, exc_info=e)
|
464
549
|
log.exception(e)
|
465
550
|
session.send_gateway_status(f"Could not login: {e}", show="busy")
|
466
551
|
session.send_gateway_message(
|
@@ -469,7 +554,7 @@ class BaseGateway(
|
|
469
554
|
)
|
470
555
|
return
|
471
556
|
|
472
|
-
log.info("Login success for %s", session.
|
557
|
+
log.info("Login success for %s", session.user_jid)
|
473
558
|
session.logged = True
|
474
559
|
session.send_gateway_status("Syncing contacts…", show="dnd")
|
475
560
|
await session.contacts.fill()
|
@@ -483,19 +568,18 @@ class BaseGateway(
|
|
483
568
|
for c in session.contacts:
|
484
569
|
# we need to receive presences directed at the contacts, in
|
485
570
|
# order to send pubsub events for their +notify features
|
486
|
-
self.send_presence(pfrom=c.jid, pto=session.
|
571
|
+
self.send_presence(pfrom=c.jid, pto=session.user_jid.bare, ptype="probe")
|
487
572
|
if status is None:
|
488
573
|
session.send_gateway_status("Logged in", show="chat")
|
489
574
|
else:
|
490
575
|
session.send_gateway_status(status, show="chat")
|
491
|
-
|
492
|
-
|
493
|
-
session.create_task(self.__fetch_user_avatar(session))
|
576
|
+
if session.user.preferences.get("sync_avatar", False):
|
577
|
+
session.create_task(self.fetch_user_avatar(session))
|
494
578
|
|
495
|
-
async def
|
579
|
+
async def fetch_user_avatar(self, session: BaseSession):
|
496
580
|
try:
|
497
581
|
iq = await self.xmpp.plugin["xep_0060"].get_items(
|
498
|
-
session.
|
582
|
+
session.user_jid.bare,
|
499
583
|
self.xmpp.plugin["xep_0084"].stanza.MetaData.namespace,
|
500
584
|
ifrom=self.boundjid.bare,
|
501
585
|
)
|
@@ -577,7 +661,7 @@ class BaseGateway(
|
|
577
661
|
)
|
578
662
|
|
579
663
|
ifrom = iq.get_from()
|
580
|
-
user =
|
664
|
+
user = self.store.users.get(ifrom)
|
581
665
|
if user is None:
|
582
666
|
raise XMPPError("registration-required")
|
583
667
|
|
@@ -629,7 +713,7 @@ class BaseGateway(
|
|
629
713
|
async def make_registration_form(self, _jid, _node, _ifrom, iq: Iq):
|
630
714
|
self.raise_if_not_allowed_jid(iq.get_from())
|
631
715
|
reg = iq["register"]
|
632
|
-
user =
|
716
|
+
user = self.store.users.get_by_stanza(iq)
|
633
717
|
log.debug("User found: %s", user)
|
634
718
|
|
635
719
|
form = reg["form"]
|
@@ -675,18 +759,20 @@ class BaseGateway(
|
|
675
759
|
reply.set_payload(reg)
|
676
760
|
return reply
|
677
761
|
|
678
|
-
async def user_prevalidate(
|
762
|
+
async def user_prevalidate(
|
763
|
+
self, ifrom: JID, form_dict: dict[str, Optional[str]]
|
764
|
+
) -> Optional[Mapping]:
|
679
765
|
# Pre validate a registration form using the content of self.REGISTRATION_FIELDS
|
680
766
|
# before passing it to the plugin custom validation logic.
|
681
767
|
for field in self.REGISTRATION_FIELDS:
|
682
768
|
if field.required and not form_dict.get(field.var):
|
683
769
|
raise ValueError(f"Missing field: '{field.label}'")
|
684
770
|
|
685
|
-
await self.validate(ifrom, form_dict)
|
771
|
+
return await self.validate(ifrom, form_dict)
|
686
772
|
|
687
773
|
async def validate(
|
688
774
|
self, user_jid: JID, registration_form: dict[str, Optional[str]]
|
689
|
-
):
|
775
|
+
) -> Optional[Mapping]:
|
690
776
|
"""
|
691
777
|
Validate a user's initial registration form.
|
692
778
|
|
@@ -706,11 +792,19 @@ class BaseGateway(
|
|
706
792
|
|
707
793
|
:param user_jid: JID of the user that has just registered
|
708
794
|
:param registration_form: A dict where keys are the :attr:`.FormField.var` attributes
|
709
|
-
|
795
|
+
of the :attr:`.BaseGateway.REGISTRATION_FIELDS` iterable.
|
796
|
+
This dict can be modified and will be accessible as the ``legacy_module_data``
|
797
|
+
of the
|
798
|
+
|
799
|
+
:return : A dict that will be stored as the persistent "legacy_module_data"
|
800
|
+
for this user. If you don't return anything here, the whole registration_form
|
801
|
+
content will be stored.
|
710
802
|
"""
|
711
803
|
raise NotImplementedError
|
712
804
|
|
713
|
-
async def validate_two_factor_code(
|
805
|
+
async def validate_two_factor_code(
|
806
|
+
self, user: GatewayUser, code: str
|
807
|
+
) -> Optional[dict]:
|
714
808
|
"""
|
715
809
|
Called when the user enters their 2FA code.
|
716
810
|
|
@@ -725,6 +819,9 @@ class BaseGateway(
|
|
725
819
|
:attr:`.registration_form` attributes to get what you need.
|
726
820
|
:param code: The code they entered, either via "chatbot" message or
|
727
821
|
adhoc command
|
822
|
+
|
823
|
+
:return : A dict which keys and values will be added to the persistent "legacy_module_data"
|
824
|
+
for this user.
|
728
825
|
"""
|
729
826
|
raise NotImplementedError
|
730
827
|
|
@@ -744,7 +841,10 @@ class BaseGateway(
|
|
744
841
|
raise NotImplementedError
|
745
842
|
|
746
843
|
async def confirm_qr(
|
747
|
-
self,
|
844
|
+
self,
|
845
|
+
user_bare_jid: str,
|
846
|
+
exception: Optional[Exception] = None,
|
847
|
+
legacy_data: Optional[dict] = None,
|
748
848
|
):
|
749
849
|
"""
|
750
850
|
This method is meant to be called to finalize QR code-based registration
|
@@ -757,10 +857,12 @@ class BaseGateway(
|
|
757
857
|
:class:`GatewayUser` instance
|
758
858
|
:param exception: Optionally, an XMPPError to be raised to **not** confirm
|
759
859
|
QR code flashing.
|
860
|
+
:param legacy_data: dict which keys and values will be added to the persistent
|
861
|
+
"legacy_module_data" for this user.
|
760
862
|
"""
|
761
863
|
fut = self.qr_pending_registrations[user_bare_jid]
|
762
864
|
if exception is None:
|
763
|
-
fut.set_result(
|
865
|
+
fut.set_result(legacy_data)
|
764
866
|
else:
|
765
867
|
fut.set_exception(exception)
|
766
868
|
|
@@ -771,14 +873,17 @@ class BaseGateway(
|
|
771
873
|
async def unregister(self, user: GatewayUser):
|
772
874
|
"""
|
773
875
|
Optionally override this if you need to clean additional
|
774
|
-
stuff after a user has been removed from the
|
876
|
+
stuff after a user has been removed from the persistent user store.
|
775
877
|
|
776
878
|
By default, this just calls :meth:`BaseSession.logout`.
|
777
879
|
|
778
880
|
:param user:
|
779
881
|
"""
|
780
882
|
session = self.get_session_from_user(user)
|
781
|
-
|
883
|
+
try:
|
884
|
+
await session.logout()
|
885
|
+
except NotImplementedError:
|
886
|
+
pass
|
782
887
|
|
783
888
|
async def input(
|
784
889
|
self, jid: JID, text=None, mtype: MessageTypes = "chat", **msg_kwargs
|
@@ -825,7 +930,7 @@ class BaseGateway(
|
|
825
930
|
# """
|
826
931
|
log.debug("Shutting down")
|
827
932
|
tasks = []
|
828
|
-
for user in
|
933
|
+
for user in self.store.users.get_all():
|
829
934
|
tasks.append(self.session_cls.from_jid(user.jid).shutdown())
|
830
935
|
self.send_presence(ptype="unavailable", pto=user.jid)
|
831
936
|
return tasks
|
slidge/core/gateway/disco.py
CHANGED
@@ -5,8 +5,6 @@ from slixmpp.exceptions import XMPPError
|
|
5
5
|
from slixmpp.plugins.xep_0030.stanza.items import DiscoItems
|
6
6
|
from slixmpp.types import OptJid
|
7
7
|
|
8
|
-
from ...util.db import user_store
|
9
|
-
|
10
8
|
if TYPE_CHECKING:
|
11
9
|
from .base import BaseGateway
|
12
10
|
|
@@ -38,7 +36,7 @@ class Disco:
|
|
38
36
|
if ifrom is None:
|
39
37
|
raise XMPPError("subscription-required")
|
40
38
|
|
41
|
-
user =
|
39
|
+
user = self.xmpp.store.users.get(ifrom)
|
42
40
|
if user is None:
|
43
41
|
raise XMPPError("registration-required")
|
44
42
|
session = self.xmpp.get_session_from_user(user)
|
@@ -63,7 +61,7 @@ class Disco:
|
|
63
61
|
if jid != self.xmpp.boundjid.bare:
|
64
62
|
return DiscoItems()
|
65
63
|
|
66
|
-
user =
|
64
|
+
user = self.xmpp.store.users.get(ifrom)
|
67
65
|
if user is None:
|
68
66
|
raise XMPPError("registration-required")
|
69
67
|
|
slidge/core/gateway/mam.py
CHANGED
@@ -3,8 +3,6 @@ from typing import TYPE_CHECKING
|
|
3
3
|
from slixmpp import CoroutineCallback, Iq, StanzaPath
|
4
4
|
from slixmpp.exceptions import XMPPError
|
5
5
|
|
6
|
-
from ...util.db import user_store
|
7
|
-
|
8
6
|
if TYPE_CHECKING:
|
9
7
|
from .base import BaseGateway
|
10
8
|
|
@@ -46,8 +44,7 @@ class Mam:
|
|
46
44
|
text="No MAM on the component itself, use a JID with a resource"
|
47
45
|
)
|
48
46
|
|
49
|
-
|
50
|
-
user = user_store.get_by_jid(ifrom)
|
47
|
+
user = self.xmpp.store.users.get(iq.get_from())
|
51
48
|
if user is None:
|
52
49
|
raise XMPPError("registration-required")
|
53
50
|
|
slidge/core/gateway/ping.py
CHANGED
@@ -4,7 +4,6 @@ from slixmpp import CoroutineCallback, Iq, StanzaPath
|
|
4
4
|
from slixmpp.exceptions import XMPPError
|
5
5
|
|
6
6
|
from ...group import LegacyMUC
|
7
|
-
from ...util.db import user_store
|
8
7
|
|
9
8
|
if TYPE_CHECKING:
|
10
9
|
from .base import BaseGateway
|
@@ -31,7 +30,7 @@ class Ping:
|
|
31
30
|
iq.reply().send()
|
32
31
|
|
33
32
|
ifrom = iq.get_from()
|
34
|
-
user =
|
33
|
+
user = self.xmpp.store.users.get(ifrom)
|
35
34
|
if user is None:
|
36
35
|
raise XMPPError("registration-required")
|
37
36
|
|
@@ -60,7 +59,7 @@ class Ping:
|
|
60
59
|
|
61
60
|
@staticmethod
|
62
61
|
def __handle_muc_ping(muc: LegacyMUC, iq: Iq):
|
63
|
-
if iq.get_from().resource in muc.
|
62
|
+
if iq.get_from().resource in muc.get_user_resources():
|
64
63
|
iq.reply().send()
|
65
64
|
else:
|
66
65
|
raise XMPPError("not-acceptable", etype="cancel", by=muc.jid)
|
slidge/core/gateway/presence.py
CHANGED
@@ -47,7 +47,7 @@ class PresenceHandlerMixin:
|
|
47
47
|
contact = await self.__get_contact(pres)
|
48
48
|
except _IsDirectedAtComponent as e:
|
49
49
|
e.session.send_gateway_message("Bye bye!")
|
50
|
-
await e.session.kill_by_jid(e.session.
|
50
|
+
await e.session.kill_by_jid(e.session.user_jid)
|
51
51
|
return
|
52
52
|
|
53
53
|
contact.is_friend = False
|
@@ -1,9 +1,12 @@
|
|
1
|
+
from __future__ import annotations
|
2
|
+
|
1
3
|
import logging
|
2
4
|
from typing import TYPE_CHECKING, Optional
|
3
5
|
|
4
6
|
from slixmpp import JID, Iq
|
7
|
+
from slixmpp.exceptions import XMPPError
|
5
8
|
|
6
|
-
from ...
|
9
|
+
from ...db import GatewayUser
|
7
10
|
|
8
11
|
if TYPE_CHECKING:
|
9
12
|
from .base import BaseGateway
|
@@ -12,42 +15,50 @@ if TYPE_CHECKING:
|
|
12
15
|
class Registration:
|
13
16
|
def __init__(self, xmpp: "BaseGateway"):
|
14
17
|
self.xmpp = xmpp
|
15
|
-
xmpp["xep_0077"].api.register(
|
16
|
-
user_store.get,
|
17
|
-
"user_get",
|
18
|
-
)
|
19
|
-
xmpp["xep_0077"].api.register(
|
20
|
-
user_store.remove,
|
21
|
-
"user_remove",
|
22
|
-
)
|
23
18
|
xmpp["xep_0077"].api.register(
|
24
19
|
self.xmpp.make_registration_form, "make_registration_form"
|
25
20
|
)
|
21
|
+
xmpp["xep_0077"].api.register(self._user_get, "user_get")
|
26
22
|
xmpp["xep_0077"].api.register(self._user_validate, "user_validate")
|
27
23
|
xmpp["xep_0077"].api.register(self._user_modify, "user_modify")
|
24
|
+
# kept for slixmpp internal API compat
|
25
|
+
# TODO: either fully use slixmpp internal API or rewrite registration without it at all
|
26
|
+
xmpp["xep_0077"].api.register(lambda *a: None, "user_remove")
|
27
|
+
|
28
|
+
def get_user(self, jid: JID) -> GatewayUser | None:
|
29
|
+
return self.xmpp.store.users.get(jid)
|
30
|
+
|
31
|
+
async def _user_get(
|
32
|
+
self, _gateway_jid, _node, ifrom: JID, iq: Iq
|
33
|
+
) -> GatewayUser | None:
|
34
|
+
if ifrom is None:
|
35
|
+
ifrom = iq.get_from()
|
36
|
+
return self.get_user(ifrom)
|
28
37
|
|
29
38
|
async def _user_validate(self, _gateway_jid, _node, ifrom: JID, iq: Iq):
|
30
|
-
"""
|
31
|
-
SliXMPP internal API stuff
|
32
|
-
"""
|
33
39
|
xmpp = self.xmpp
|
34
40
|
log.debug("User validate: %s", ifrom.bare)
|
35
41
|
form_dict = {f.var: iq.get(f.var) for f in xmpp.REGISTRATION_FIELDS}
|
36
42
|
xmpp.raise_if_not_allowed_jid(ifrom)
|
37
|
-
await xmpp.user_prevalidate(ifrom, form_dict)
|
38
|
-
|
39
|
-
|
43
|
+
legacy_module_data = await xmpp.user_prevalidate(ifrom, form_dict)
|
44
|
+
if legacy_module_data is None:
|
45
|
+
legacy_module_data = form_dict
|
46
|
+
user = self.xmpp.store.users.new(
|
47
|
+
jid=ifrom,
|
48
|
+
legacy_module_data=legacy_module_data, # type:ignore
|
49
|
+
)
|
50
|
+
log.info("New user: %s", user)
|
40
51
|
|
41
52
|
async def _user_modify(
|
42
53
|
self, _gateway_jid, _node, ifrom: JID, form_dict: dict[str, Optional[str]]
|
43
54
|
):
|
44
|
-
"""
|
45
|
-
SliXMPP internal API stuff
|
46
|
-
"""
|
47
|
-
user = user_store.get_by_jid(ifrom)
|
48
|
-
log.debug("Modify user: %s", user)
|
49
55
|
await self.xmpp.user_prevalidate(ifrom, form_dict)
|
50
|
-
|
56
|
+
log.debug("Modify user: %s", ifrom)
|
57
|
+
user = self.xmpp.store.users.get(ifrom)
|
58
|
+
if user is None:
|
59
|
+
raise XMPPError("internal-server-error", "User not found")
|
60
|
+
user.legacy_module_data.update(form_dict)
|
61
|
+
self.xmpp.store.users.update(user)
|
51
62
|
|
52
63
|
|
53
64
|
log = logging.getLogger(__name__)
|
slidge/core/gateway/search.py
CHANGED
@@ -3,8 +3,6 @@ from typing import TYPE_CHECKING
|
|
3
3
|
from slixmpp import JID, CoroutineCallback, Iq, StanzaPath
|
4
4
|
from slixmpp.exceptions import XMPPError
|
5
5
|
|
6
|
-
from ...util.db import user_store
|
7
|
-
|
8
6
|
if TYPE_CHECKING:
|
9
7
|
from .base import BaseGateway
|
10
8
|
|
@@ -29,7 +27,7 @@ class Search:
|
|
29
27
|
"""
|
30
28
|
Prepare the search form using :attr:`.BaseSession.SEARCH_FIELDS`
|
31
29
|
"""
|
32
|
-
user =
|
30
|
+
user = self.xmpp.store.users.get(ifrom)
|
33
31
|
if user is None:
|
34
32
|
raise XMPPError(text="Search is only allowed for registered users")
|
35
33
|
|
@@ -47,7 +45,7 @@ class Search:
|
|
47
45
|
"""
|
48
46
|
Handles a search request
|
49
47
|
"""
|
50
|
-
user =
|
48
|
+
user = self.xmpp.store.users.get(ifrom)
|
51
49
|
if user is None:
|
52
50
|
raise XMPPError(text="Search is only allowed for registered users")
|
53
51
|
|
@@ -70,7 +68,7 @@ class Search:
|
|
70
68
|
if iq.get_to() != self.xmpp.boundjid.bare:
|
71
69
|
raise XMPPError("bad-request", "This can only be used on the component JID")
|
72
70
|
|
73
|
-
user =
|
71
|
+
user = self.xmpp.store.users.get(iq.get_from())
|
74
72
|
if user is None:
|
75
73
|
raise XMPPError("not-authorized", "Register to the gateway first")
|
76
74
|
|