slidge 0.1.0rc1__py3-none-any.whl → 0.1.1__py3-none-any.whl

Sign up to get free protection for your applications and to get access to all the features.
Files changed (164) hide show
  1. slidge/__init__.py +54 -31
  2. slidge/__main__.py +51 -5
  3. slidge/command/__init__.py +28 -0
  4. slidge/command/adhoc.py +258 -0
  5. slidge/command/admin.py +193 -0
  6. slidge/command/base.py +441 -0
  7. slidge/command/categories.py +3 -0
  8. slidge/command/chat_command.py +288 -0
  9. slidge/command/register.py +179 -0
  10. slidge/command/user.py +250 -0
  11. slidge/contact/__init__.py +8 -0
  12. slidge/contact/contact.py +452 -0
  13. slidge/contact/roster.py +192 -0
  14. slidge/core/__init__.py +2 -0
  15. slidge/core/cache.py +121 -39
  16. slidge/core/config.py +116 -11
  17. slidge/core/gateway/__init__.py +3 -0
  18. slidge/core/gateway/base.py +895 -0
  19. slidge/core/gateway/caps.py +63 -0
  20. slidge/core/gateway/delivery_receipt.py +52 -0
  21. slidge/core/gateway/disco.py +80 -0
  22. slidge/core/gateway/mam.py +75 -0
  23. slidge/core/gateway/muc_admin.py +35 -0
  24. slidge/core/gateway/ping.py +66 -0
  25. slidge/core/gateway/presence.py +95 -0
  26. slidge/core/gateway/registration.py +53 -0
  27. slidge/core/gateway/search.py +102 -0
  28. slidge/core/gateway/session_dispatcher.py +789 -0
  29. slidge/core/gateway/vcard_temp.py +130 -0
  30. slidge/core/mixins/__init__.py +9 -1
  31. slidge/core/mixins/attachment.py +506 -0
  32. slidge/core/mixins/avatar.py +167 -0
  33. slidge/core/mixins/base.py +6 -19
  34. slidge/core/mixins/disco.py +66 -15
  35. slidge/core/mixins/lock.py +31 -0
  36. slidge/core/mixins/message.py +254 -252
  37. slidge/core/mixins/message_maker.py +154 -0
  38. slidge/core/mixins/presence.py +128 -31
  39. slidge/core/mixins/recipient.py +43 -0
  40. slidge/core/pubsub.py +275 -116
  41. slidge/core/session.py +586 -518
  42. slidge/group/__init__.py +10 -0
  43. slidge/group/archive.py +125 -0
  44. slidge/group/bookmarks.py +163 -0
  45. slidge/group/participant.py +458 -0
  46. slidge/group/room.py +1103 -0
  47. slidge/migration.py +18 -0
  48. slidge/slixfix/__init__.py +68 -0
  49. slidge/{util/xep_0050 → slixfix/link_preview}/__init__.py +4 -5
  50. slidge/slixfix/link_preview/link_preview.py +17 -0
  51. slidge/slixfix/link_preview/stanza.py +99 -0
  52. slidge/slixfix/roster.py +60 -0
  53. slidge/{util → slixfix}/xep_0077/register.py +1 -2
  54. slidge/slixfix/xep_0077/stanza.py +104 -0
  55. slidge/{util → slixfix}/xep_0100/gateway.py +17 -12
  56. slidge/slixfix/xep_0153/__init__.py +10 -0
  57. slidge/slixfix/xep_0153/stanza.py +25 -0
  58. slidge/slixfix/xep_0153/vcard_avatar.py +23 -0
  59. slidge/slixfix/xep_0264/__init__.py +5 -0
  60. slidge/slixfix/xep_0264/stanza.py +36 -0
  61. slidge/slixfix/xep_0264/thumbnail.py +23 -0
  62. slidge/slixfix/xep_0292/__init__.py +5 -0
  63. slidge/slixfix/xep_0292/vcard4.py +100 -0
  64. slidge/slixfix/xep_0313/__init__.py +12 -0
  65. slidge/slixfix/xep_0313/mam.py +262 -0
  66. slidge/slixfix/xep_0313/stanza.py +359 -0
  67. slidge/slixfix/xep_0317/__init__.py +5 -0
  68. slidge/slixfix/xep_0317/hats.py +17 -0
  69. slidge/slixfix/xep_0317/stanza.py +28 -0
  70. slidge/{util → slixfix}/xep_0356_old/privilege.py +9 -7
  71. slidge/slixfix/xep_0424/__init__.py +9 -0
  72. slidge/slixfix/xep_0424/retraction.py +77 -0
  73. slidge/slixfix/xep_0424/stanza.py +28 -0
  74. slidge/slixfix/xep_0490/__init__.py +8 -0
  75. slidge/slixfix/xep_0490/mds.py +47 -0
  76. slidge/slixfix/xep_0490/stanza.py +17 -0
  77. slidge/util/__init__.py +4 -6
  78. slidge/util/archive_msg.py +61 -0
  79. slidge/util/conf.py +25 -4
  80. slidge/util/db.py +23 -69
  81. slidge/util/schema.sql +126 -0
  82. slidge/util/sql.py +508 -0
  83. slidge/util/test.py +136 -86
  84. slidge/util/types.py +155 -14
  85. slidge/util/util.py +225 -51
  86. slidge-0.1.1.dist-info/METADATA +110 -0
  87. slidge-0.1.1.dist-info/RECORD +96 -0
  88. {slidge-0.1.0rc1.dist-info → slidge-0.1.1.dist-info}/WHEEL +1 -1
  89. slidge/core/adhoc.py +0 -492
  90. slidge/core/chat_command.py +0 -197
  91. slidge/core/contact.py +0 -441
  92. slidge/core/disco.py +0 -59
  93. slidge/core/gateway.py +0 -899
  94. slidge/core/muc/__init__.py +0 -3
  95. slidge/core/muc/bookmarks.py +0 -74
  96. slidge/core/muc/participant.py +0 -152
  97. slidge/core/muc/room.py +0 -348
  98. slidge/plugins/discord/__init__.py +0 -121
  99. slidge/plugins/discord/client.py +0 -121
  100. slidge/plugins/discord/session.py +0 -172
  101. slidge/plugins/dummy.py +0 -334
  102. slidge/plugins/facebook.py +0 -591
  103. slidge/plugins/hackernews.py +0 -209
  104. slidge/plugins/mattermost/__init__.py +0 -1
  105. slidge/plugins/mattermost/api.py +0 -288
  106. slidge/plugins/mattermost/gateway.py +0 -417
  107. slidge/plugins/mattermost/websocket.py +0 -248
  108. slidge/plugins/signal/__init__.py +0 -4
  109. slidge/plugins/signal/config.py +0 -4
  110. slidge/plugins/signal/contact.py +0 -104
  111. slidge/plugins/signal/gateway.py +0 -379
  112. slidge/plugins/signal/group.py +0 -76
  113. slidge/plugins/signal/session.py +0 -515
  114. slidge/plugins/signal/txt.py +0 -13
  115. slidge/plugins/signal/util.py +0 -32
  116. slidge/plugins/skype.py +0 -310
  117. slidge/plugins/steam.py +0 -400
  118. slidge/plugins/telegram/__init__.py +0 -6
  119. slidge/plugins/telegram/client.py +0 -325
  120. slidge/plugins/telegram/config.py +0 -21
  121. slidge/plugins/telegram/contact.py +0 -154
  122. slidge/plugins/telegram/gateway.py +0 -182
  123. slidge/plugins/telegram/group.py +0 -184
  124. slidge/plugins/telegram/session.py +0 -275
  125. slidge/plugins/telegram/util.py +0 -153
  126. slidge/plugins/whatsapp/__init__.py +0 -6
  127. slidge/plugins/whatsapp/config.py +0 -17
  128. slidge/plugins/whatsapp/contact.py +0 -33
  129. slidge/plugins/whatsapp/event.go +0 -455
  130. slidge/plugins/whatsapp/gateway.go +0 -156
  131. slidge/plugins/whatsapp/gateway.py +0 -69
  132. slidge/plugins/whatsapp/go.mod +0 -17
  133. slidge/plugins/whatsapp/go.sum +0 -22
  134. slidge/plugins/whatsapp/session.go +0 -371
  135. slidge/plugins/whatsapp/session.py +0 -370
  136. slidge/util/xep_0030/__init__.py +0 -13
  137. slidge/util/xep_0030/disco.py +0 -811
  138. slidge/util/xep_0030/stanza/__init__.py +0 -7
  139. slidge/util/xep_0030/stanza/info.py +0 -270
  140. slidge/util/xep_0030/stanza/items.py +0 -147
  141. slidge/util/xep_0030/static.py +0 -467
  142. slidge/util/xep_0050/adhoc.py +0 -631
  143. slidge/util/xep_0050/stanza.py +0 -180
  144. slidge/util/xep_0077/stanza.py +0 -71
  145. slidge/util/xep_0292/__init__.py +0 -1
  146. slidge/util/xep_0292/stanza.py +0 -167
  147. slidge/util/xep_0292/vcard4.py +0 -74
  148. slidge/util/xep_0356/__init__.py +0 -7
  149. slidge/util/xep_0356/permissions.py +0 -35
  150. slidge/util/xep_0356/privilege.py +0 -160
  151. slidge/util/xep_0356/stanza.py +0 -44
  152. slidge/util/xep_0461/__init__.py +0 -6
  153. slidge/util/xep_0461/reply.py +0 -48
  154. slidge/util/xep_0461/stanza.py +0 -80
  155. slidge-0.1.0rc1.dist-info/METADATA +0 -171
  156. slidge-0.1.0rc1.dist-info/RECORD +0 -99
  157. /slidge/{plugins/__init__.py → py.typed} +0 -0
  158. /slidge/{util → slixfix}/xep_0077/__init__.py +0 -0
  159. /slidge/{util → slixfix}/xep_0100/__init__.py +0 -0
  160. /slidge/{util → slixfix}/xep_0100/stanza.py +0 -0
  161. /slidge/{util → slixfix}/xep_0356_old/__init__.py +0 -0
  162. /slidge/{util → slixfix}/xep_0356_old/stanza.py +0 -0
  163. {slidge-0.1.0rc1.dist-info → slidge-0.1.1.dist-info}/LICENSE +0 -0
  164. {slidge-0.1.0rc1.dist-info → slidge-0.1.1.dist-info}/entry_points.txt +0 -0
slidge/migration.py ADDED
@@ -0,0 +1,18 @@
1
+ import logging
2
+ import shutil
3
+
4
+ from .core import config
5
+
6
+
7
+ def remove_avatar_cache_v1():
8
+ old_dir = config.HOME_DIR / "slidge_avatars"
9
+ if old_dir.exists():
10
+ log.info("Avatar cache dir v1 found, clearing it.")
11
+ shutil.rmtree(old_dir)
12
+
13
+
14
+ def migrate():
15
+ remove_avatar_cache_v1()
16
+
17
+
18
+ log = logging.getLogger(__name__)
@@ -0,0 +1,68 @@
1
+ # This module contains patches for slixmpp; some have pending requests upstream
2
+ # and should be removed on the next slixmpp release.
3
+
4
+ # ruff: noqa: F401
5
+
6
+ import slixmpp.plugins
7
+ from slixmpp import Message
8
+ from slixmpp.plugins.xep_0050 import XEP_0050, Command
9
+ from slixmpp.xmlstream import StanzaBase
10
+
11
+ from . import ( # xep_0356,
12
+ link_preview,
13
+ xep_0077,
14
+ xep_0100,
15
+ xep_0153,
16
+ xep_0264,
17
+ xep_0292,
18
+ xep_0313,
19
+ xep_0317,
20
+ xep_0356_old,
21
+ xep_0424,
22
+ xep_0490,
23
+ )
24
+
25
+
26
+ def session_bind(self, jid):
27
+ self.xmpp["xep_0030"].add_feature(Command.namespace)
28
+ # awful hack to for the disco items: we need to comment this line
29
+ # related issue: https://todo.sr.ht/~nicoco/slidge/131
30
+ # self.xmpp['xep_0030'].set_items(node=Command.namespace, items=tuple())
31
+
32
+
33
+ XEP_0050.session_bind = session_bind # type:ignore
34
+
35
+
36
+ def reply(self, body=None, clear=True):
37
+ """
38
+ Overrides slixmpp's Message.reply(), since it strips to sender's resource
39
+ for mtype=groupchat, and we do not want that, because when we raise an XMPPError,
40
+ we actually want to preserve the resource.
41
+ (this is called in RootStanza.exception() to handle XMPPErrors)
42
+ """
43
+ new_message = StanzaBase.reply(self, clear)
44
+ new_message["thread"] = self["thread"]
45
+ new_message["parent_thread"] = self["parent_thread"]
46
+
47
+ del new_message["id"]
48
+ if self.stream is not None and self.stream.use_message_ids:
49
+ new_message["id"] = self.stream.new_id()
50
+
51
+ if body is not None:
52
+ new_message["body"] = body
53
+ return new_message
54
+
55
+
56
+ slixmpp.plugins.PLUGINS.extend(
57
+ [
58
+ "link_preview",
59
+ "xep_0264",
60
+ "xep_0292_provider",
61
+ "xep_0317",
62
+ "xep_0356_old",
63
+ "xep_0490",
64
+ ]
65
+ )
66
+
67
+
68
+ Message.reply = reply # type: ignore
@@ -1,11 +1,10 @@
1
-
2
1
  # Slixmpp: The Slick XMPP Library
3
- # Copyright (C) 2011 Nathanael C. Fritz, Lance J.T. Stout
2
+ # Copyright (C) 2012 Nathanael C. Fritz, Lance J.T. Stout
4
3
  # This file is part of Slixmpp.
5
4
  # See the file LICENSE for copying permission.
5
+
6
6
  from slixmpp.plugins.base import register_plugin
7
7
 
8
- from .adhoc import XEP_0050
9
- from .stanza import Command
8
+ from .link_preview import LinkPreview
10
9
 
11
- register_plugin(XEP_0050)
10
+ register_plugin(LinkPreview)
@@ -0,0 +1,17 @@
1
+ # Slixmpp: The Slick XMPP Library
2
+ # Copyright (C) 2012 Nathanael C. Fritz, Lance J.T. Stout
3
+ # This file is part of Slixmpp.
4
+ # See the file LICENSE for copying permission.
5
+ from slixmpp.plugins import BasePlugin
6
+
7
+ from . import stanza
8
+
9
+
10
+ class LinkPreview(BasePlugin):
11
+ name = "link_preview"
12
+ description = "Sender-generated link previews"
13
+ dependencies = set()
14
+ stanza = stanza
15
+
16
+ def plugin_init(self):
17
+ stanza.register_plugin()
@@ -0,0 +1,99 @@
1
+ # Slixmpp: The Slick XMPP Library
2
+ # Copyright (C) 2012 Nathanael C. Fritz, Lance J.T. Stout
3
+ # This file is part of Slixmpp.
4
+ # See the file LICENSE for copying permission.
5
+ from typing import Optional, Type
6
+
7
+ from slixmpp.stanza.message import Message
8
+ from slixmpp.xmlstream import ElementBase, register_stanza_plugin
9
+
10
+
11
+ class LinkPreview(ElementBase):
12
+ name = "Description"
13
+ namespace = "http://www.w3.org/1999/02/22-rdf-syntax-ns#"
14
+ plugin_attrib = "link_preview"
15
+ plugin_multi_attrib = "link_previews"
16
+ interfaces = {"about", "title", "description", "url", "image", "type", "site_name"}
17
+
18
+ def _set_og(self, el: ElementBase, value: str) -> None:
19
+ el.xml.text = value
20
+ self.xml.append(el.xml)
21
+
22
+ def _get_og(self, el: Type[ElementBase]) -> Optional[str]:
23
+ child = self.xml.find(f"{{{el.namespace}}}{el.name}")
24
+ if child is None:
25
+ return None
26
+ return child.text
27
+
28
+ def set_title(self, v: str) -> None:
29
+ self._set_og(Title(), v)
30
+
31
+ def get_title(self) -> Optional[str]:
32
+ return self._get_og(Title)
33
+
34
+ def set_description(self, v: str) -> None:
35
+ self._set_og(Description(), v)
36
+
37
+ def get_description(self) -> Optional[str]:
38
+ return self._get_og(Description)
39
+
40
+ def set_url(self, v: str) -> None:
41
+ self._set_og(Url(), v)
42
+
43
+ def get_url(self) -> Optional[str]:
44
+ return self._get_og(Url)
45
+
46
+ def set_image(self, v: str) -> None:
47
+ self._set_og(Image(), v)
48
+
49
+ def get_image(self) -> Optional[str]:
50
+ return self._get_og(Image)
51
+
52
+ def set_type(self, v: str) -> None:
53
+ self._set_og(Type_(), v)
54
+
55
+ def get_type(self) -> Optional[str]:
56
+ return self._get_og(Type_)
57
+
58
+ def set_site_name(self, v: str) -> None:
59
+ self._set_og(SiteName(), v)
60
+
61
+ def get_site_name(self) -> Optional[str]:
62
+ return self._get_og(SiteName)
63
+
64
+ def get_about(self) -> Optional[str]:
65
+ return self.xml.attrib.get(f"{{{self.namespace}}}about")
66
+
67
+
68
+ class OpenGraphMixin(ElementBase):
69
+ namespace = "https://ogp.me/ns#"
70
+
71
+
72
+ class Title(OpenGraphMixin):
73
+ name = plugin_attrib = "title"
74
+
75
+
76
+ class Description(OpenGraphMixin):
77
+ name = plugin_attrib = "description"
78
+
79
+
80
+ class Url(OpenGraphMixin):
81
+ name = plugin_attrib = "url"
82
+
83
+
84
+ class Image(OpenGraphMixin):
85
+ name = plugin_attrib = "image"
86
+
87
+
88
+ class Type_(OpenGraphMixin):
89
+ name = plugin_attrib = "type"
90
+
91
+
92
+ class SiteName(OpenGraphMixin):
93
+ name = plugin_attrib = "site_name"
94
+
95
+
96
+ def register_plugin():
97
+ for plugin in Title, Description, Url, Image, Type_, SiteName:
98
+ register_stanza_plugin(plugin, Title)
99
+ register_stanza_plugin(Message, LinkPreview, iterable=True)
@@ -0,0 +1,60 @@
1
+ from slixmpp import JID
2
+
3
+ from ..util.db import log, user_store
4
+
5
+
6
+ class YesSet(set):
7
+ """
8
+ A pseudo-set which always test True for membership
9
+ """
10
+
11
+ def __contains__(self, item):
12
+ log.debug("Test in")
13
+ return True
14
+
15
+
16
+ class RosterBackend:
17
+ """
18
+ A pseudo-roster for the gateway component.
19
+
20
+ If a user is in the user store, this will behave as if the user is part of the
21
+ roster with subscription "both", and "none" otherwise.
22
+
23
+ This is rudimentary but the only sane way I could come up with so far.
24
+ """
25
+
26
+ @staticmethod
27
+ def entries(_owner_jid, _default=None):
28
+ return YesSet()
29
+
30
+ @staticmethod
31
+ def save(_owner_jid, _jid, _item_state, _db_state):
32
+ pass
33
+
34
+ @staticmethod
35
+ def load(_owner_jid, jid, _db_state):
36
+ log.debug("Load %s", jid)
37
+ user = user_store.get_by_jid(JID(jid))
38
+ log.debug("User %s", user)
39
+ if user is None:
40
+ return {
41
+ "name": "",
42
+ "groups": [],
43
+ "from": False,
44
+ "to": False,
45
+ "pending_in": False,
46
+ "pending_out": False,
47
+ "whitelisted": False,
48
+ "subscription": "both",
49
+ }
50
+ else:
51
+ return {
52
+ "name": "",
53
+ "groups": [],
54
+ "from": True,
55
+ "to": True,
56
+ "pending_in": False,
57
+ "pending_out": False,
58
+ "whitelisted": False,
59
+ "subscription": "none",
60
+ }
@@ -20,7 +20,6 @@ log = logging.getLogger(__name__)
20
20
 
21
21
  # noinspection PyPep8Naming
22
22
  class XEP_0077(BasePlugin):
23
-
24
23
  """
25
24
  XEP-0077: In-Band Registration
26
25
 
@@ -81,7 +80,7 @@ class XEP_0077(BasePlugin):
81
80
  self.xmpp.register_handler(
82
81
  CoroutineCallback(
83
82
  "registration",
84
- StanzaPath("/iq/register"),
83
+ StanzaPath(f"/iq@to={self.xmpp.boundjid.bare}/register"),
85
84
  self._handle_registration,
86
85
  )
87
86
  )
@@ -0,0 +1,104 @@
1
+ # Slixmpp: The Slick XMPP Library
2
+ # Copyright (C) 2012 Nathanael C. Fritz, Lance J.T. Stout
3
+ # This file is part of Slixmpp.
4
+ # See the file LICENSE for copying permission.
5
+ from __future__ import unicode_literals
6
+
7
+ from typing import ClassVar, Set
8
+
9
+ from slixmpp.xmlstream import ElementBase
10
+
11
+
12
+ class Register(ElementBase):
13
+ namespace = "jabber:iq:register"
14
+ name = "query"
15
+ plugin_attrib = "register"
16
+ interfaces = {
17
+ "username",
18
+ "password",
19
+ "email",
20
+ "nick",
21
+ "name",
22
+ "first",
23
+ "last",
24
+ "address",
25
+ "city",
26
+ "state",
27
+ "zip",
28
+ "phone",
29
+ "url",
30
+ "date",
31
+ "misc",
32
+ "text",
33
+ "key",
34
+ "registered",
35
+ "remove",
36
+ "instructions",
37
+ "fields",
38
+ }
39
+ sub_interfaces = interfaces
40
+ form_fields = {
41
+ "username",
42
+ "password",
43
+ "email",
44
+ "nick",
45
+ "name",
46
+ "first",
47
+ "last",
48
+ "address",
49
+ "city",
50
+ "state",
51
+ "zip",
52
+ "phone",
53
+ "url",
54
+ "date",
55
+ "misc",
56
+ "text",
57
+ "key",
58
+ }
59
+
60
+ def get_registered(self):
61
+ present = self.xml.find("{%s}registered" % self.namespace)
62
+ return present is not None
63
+
64
+ def get_remove(self):
65
+ present = self.xml.find("{%s}remove" % self.namespace)
66
+ return present is not None
67
+
68
+ def set_registered(self, value):
69
+ if value:
70
+ self.add_field("registered")
71
+ else:
72
+ del self["registered"]
73
+
74
+ def set_remove(self, value):
75
+ if value:
76
+ self.add_field("remove")
77
+ else:
78
+ del self["remove"]
79
+
80
+ def add_field(self, value):
81
+ self._set_sub_text(value, "", keep=True)
82
+
83
+ def get_fields(self):
84
+ fields = set()
85
+ for field in self.form_fields:
86
+ if self.xml.find("{%s}%s" % (self.namespace, field)) is not None:
87
+ fields.add(field)
88
+ return fields
89
+
90
+ def set_fields(self, fields):
91
+ del self["fields"]
92
+ for field in fields:
93
+ self._set_sub_text(field, "", keep=True)
94
+
95
+ def del_fields(self):
96
+ for field in self.form_fields:
97
+ self._del_sub(field)
98
+
99
+
100
+ class RegisterFeature(ElementBase):
101
+ name = "register"
102
+ namespace = "http://jabber.org/features/iq-register"
103
+ plugin_attrib = name
104
+ interfaces: ClassVar[Set[str]] = set()
@@ -1,6 +1,8 @@
1
1
  import logging
2
+ import warnings
2
3
 
3
4
  from slixmpp import JID, Iq, Message, Presence, register_stanza_plugin
5
+ from slixmpp.exceptions import XMPPError
4
6
  from slixmpp.plugins.base import BasePlugin
5
7
 
6
8
  from slidge.core import config
@@ -80,20 +82,21 @@ class XEP_0100(BasePlugin):
80
82
  "groups": ["Slidge"],
81
83
  }
82
84
  }
85
+ try:
86
+ await self._set_roster(jid, items)
87
+ except PermissionError:
88
+ warnings.warn(
89
+ "Slidge does not have the privilege to manage users' rosters. "
90
+ "Users should add the slidge component to their rosters manually."
91
+ )
92
+ if config.ROSTER_PUSH_PRESENCE_SUBSCRIPTION_REQUEST_FALLBACK:
93
+ self.xmpp.send_presence(ptype="subscribe", pto=jid.bare)
94
+
95
+ async def _set_roster(self, jid, items):
83
96
  try:
84
97
  await self.xmpp["xep_0356"].set_roster(jid=jid.bare, roster_items=items)
85
98
  except PermissionError:
86
- try:
87
- await self.xmpp["xep_0356_old"].set_roster(
88
- jid=jid.bare, roster_items=items
89
- )
90
- except PermissionError:
91
- log.warning(
92
- "Slidge does not have the privilege to manage users' rosters. "
93
- "Users should add the slidge component to their rosters manually."
94
- )
95
- if config.ROSTER_PUSH_PRESENCE_SUBSCRIPTION_REQUEST_FALLBACK:
96
- self.xmpp.send_presence(ptype="subscribe", pto=jid.bare)
99
+ await self.xmpp["xep_0356_old"].set_roster(jid=jid.bare, roster_items=items)
97
100
 
98
101
  def on_presence_unsubscribe(self, p: Presence):
99
102
  if p.get_to() == self.xmpp.boundjid.bare:
@@ -111,6 +114,8 @@ class XEP_0100(BasePlugin):
111
114
  return
112
115
 
113
116
  if self.needs_registration and await self.get_user(msg) is None:
114
- return
117
+ raise XMPPError(
118
+ "registration-required", text="You are not registered to this gateway"
119
+ )
115
120
 
116
121
  self.xmpp.event("legacy_message", msg)
@@ -0,0 +1,10 @@
1
+ # Slixmpp: The Slick XMPP Library
2
+ # Copyright (C) 2012 Nathanael C. Fritz, Lance J.T. Stout
3
+ # This file is part of Slixmpp.
4
+ # See the file LICENSE for copying permission.
5
+ from slixmpp.plugins.base import register_plugin
6
+
7
+ from .stanza import VCardTempUpdate
8
+ from .vcard_avatar import XEP_0153
9
+
10
+ register_plugin(XEP_0153)
@@ -0,0 +1,25 @@
1
+ # Slixmpp: The Slick XMPP Library
2
+ # Copyright (C) 2012 Nathanael C. Fritz, Lance J.T. Stout
3
+ # This file is part of Slixmpp.
4
+ # See the file LICENSE for copying permission.
5
+ from slixmpp.xmlstream import ElementBase
6
+
7
+
8
+ class VCardTempUpdate(ElementBase):
9
+ name = "x"
10
+ namespace = "vcard-temp:x:update"
11
+ plugin_attrib = "vcard_temp_update"
12
+ interfaces = {"photo"}
13
+ sub_interfaces = interfaces
14
+
15
+ def set_photo(self, value):
16
+ if value is not None:
17
+ self._set_sub_text("photo", value, keep=True)
18
+ else:
19
+ self._del_sub("photo")
20
+
21
+ def get_photo(self):
22
+ photo = self.xml.find("{%s}photo" % self.namespace)
23
+ if photo is None:
24
+ return None
25
+ return photo.text
@@ -0,0 +1,23 @@
1
+ # Slixmpp: The Slick XMPP Library
2
+ # Copyright (C) 2012 Nathanael C. Fritz, Lance J.T. Stout
3
+ # This file is part of Slixmpp.
4
+ # See the file LICENSE for copying permission.
5
+ import logging
6
+
7
+ from slixmpp.plugins.base import BasePlugin
8
+ from slixmpp.stanza import Presence
9
+ from slixmpp.xmlstream import register_stanza_plugin
10
+
11
+ from . import VCardTempUpdate, stanza
12
+
13
+ log = logging.getLogger(__name__)
14
+
15
+
16
+ class XEP_0153(BasePlugin):
17
+ name = "xep_0153"
18
+ description = "XEP-0153: vCard-Based Avatars (slidge, just for MUCs)"
19
+ dependencies = {"xep_0054"}
20
+ stanza = stanza
21
+
22
+ def plugin_init(self):
23
+ register_stanza_plugin(Presence, VCardTempUpdate)
@@ -0,0 +1,5 @@
1
+ from slixmpp.plugins.base import register_plugin
2
+
3
+ from .thumbnail import XEP_0264
4
+
5
+ register_plugin(XEP_0264)
@@ -0,0 +1,36 @@
1
+ from typing import Optional
2
+
3
+ from slixmpp import register_stanza_plugin
4
+ from slixmpp.plugins.xep_0234.stanza import File
5
+ from slixmpp.xmlstream import ElementBase
6
+
7
+ NS = "urn:xmpp:thumbs:1"
8
+
9
+
10
+ class Thumbnail(ElementBase):
11
+ name = plugin_attrib = "thumbnail"
12
+ namespace = NS
13
+ interfaces = {"uri", "media-type", "width", "height"}
14
+
15
+ def get_width(self) -> float:
16
+ return _int_or_none(self._get_attr("width"))
17
+
18
+ def get_height(self) -> float:
19
+ return _int_or_none(self._get_attr("height"))
20
+
21
+ def set_width(self, v: int) -> None:
22
+ self._set_attr("width", str(v))
23
+
24
+ def set_height(self, v: int) -> None:
25
+ self._set_attr("height", str(v))
26
+
27
+
28
+ def _int_or_none(v) -> Optional[int]:
29
+ try:
30
+ return int(v)
31
+ except ValueError:
32
+ return None
33
+
34
+
35
+ def register_plugin():
36
+ register_stanza_plugin(File, Thumbnail)
@@ -0,0 +1,23 @@
1
+ import logging
2
+
3
+ from slixmpp.plugins import BasePlugin
4
+
5
+ from . import stanza
6
+
7
+ log = logging.getLogger(__name__)
8
+
9
+
10
+ class XEP_0264(BasePlugin):
11
+ """
12
+ XEP-0264: Jingle Content Thumbnails
13
+
14
+ Minimum needed for xep 0385 (Stateless inline media sharing)
15
+ """
16
+
17
+ name = "xep_0264"
18
+ description = "XEP-0264: Jingle Content Thumbnails"
19
+ dependencies = {"xep_0234"}
20
+ stanza = stanza
21
+
22
+ def plugin_init(self):
23
+ stanza.register_plugin()
@@ -0,0 +1,5 @@
1
+ from slixmpp.plugins.xep_0292 import stanza
2
+
3
+ from . import vcard4
4
+
5
+ __all__ = ("stanza", "vcard4")
@@ -0,0 +1,100 @@
1
+ import logging
2
+ from typing import TYPE_CHECKING, NamedTuple, Optional
3
+
4
+ from slixmpp import JID, CoroutineCallback, Iq, StanzaPath
5
+ from slixmpp.plugins.base import BasePlugin, register_plugin
6
+ from slixmpp.plugins.xep_0292.stanza import NS, VCard4
7
+ from slixmpp.types import JidStr
8
+
9
+ from slidge.contact import LegacyContact
10
+
11
+ if TYPE_CHECKING:
12
+ from slidge.core.gateway import BaseGateway
13
+
14
+
15
+ class StoredVCard(NamedTuple):
16
+ content: VCard4
17
+ authorized_jids: set[JidStr]
18
+
19
+
20
+ class VCard4Provider(BasePlugin):
21
+ xmpp: "BaseGateway"
22
+
23
+ name = "xep_0292_provider"
24
+ description = "VCard4 Provider"
25
+ dependencies = {"xep_0030"}
26
+
27
+ def __init__(self, *a, **k):
28
+ super(VCard4Provider, self).__init__(*a, **k)
29
+ self._vcards = dict[JidStr, StoredVCard]()
30
+
31
+ def plugin_init(self):
32
+ self.xmpp.register_handler(
33
+ CoroutineCallback(
34
+ "get_vcard",
35
+ StanzaPath(f"iq@type=get/vcard"),
36
+ self.handle_vcard_get, # type:ignore
37
+ )
38
+ )
39
+
40
+ self.xmpp.plugin["xep_0030"].add_feature(NS)
41
+
42
+ def _get_cached_vcard(self, jid: JidStr, requested_by: JidStr) -> Optional[VCard4]:
43
+ vcard = self._vcards.get(JID(jid).bare)
44
+ if vcard:
45
+ if auth := vcard.authorized_jids:
46
+ if JID(requested_by).bare in auth:
47
+ return vcard.content
48
+ else:
49
+ return vcard.content
50
+ return None
51
+
52
+ async def get_vcard(self, jid: JidStr, requested_by: JidStr) -> Optional[VCard4]:
53
+ if vcard := self._get_cached_vcard(jid, requested_by):
54
+ log.debug("Found a cached vcard")
55
+ return vcard
56
+ if not hasattr(self.xmpp, "get_session_from_jid"):
57
+ return None
58
+ jid = JID(jid)
59
+ requested_by = JID(requested_by)
60
+ session = self.xmpp.get_session_from_jid(requested_by)
61
+ if session is None:
62
+ return
63
+ entity = await session.get_contact_or_group_or_participant(jid)
64
+ if isinstance(entity, LegacyContact):
65
+ log.debug("Fetching vcard")
66
+ await entity.fetch_vcard()
67
+ return self._get_cached_vcard(jid, requested_by)
68
+ return None
69
+
70
+ async def handle_vcard_get(self, iq: Iq):
71
+ r = iq.reply()
72
+ if vcard := await self.get_vcard(iq.get_to().bare, iq.get_from().bare):
73
+ r.append(vcard)
74
+ else:
75
+ r.enable("vcard")
76
+ r.send()
77
+
78
+ def set_vcard(
79
+ self,
80
+ jid: JidStr,
81
+ vcard: VCard4,
82
+ /,
83
+ authorized_jids: Optional[set[JidStr]] = None,
84
+ ):
85
+ cache = self._vcards.get(jid)
86
+ new = StoredVCard(
87
+ vcard, authorized_jids if authorized_jids is not None else set()
88
+ )
89
+ self._vcards[jid] = new
90
+ if cache == new:
91
+ return
92
+ if self.xmpp["pubsub"] and authorized_jids:
93
+ for to in authorized_jids:
94
+ self.xmpp.loop.create_task(
95
+ self.xmpp["pubsub"].broadcast_vcard_event(jid, to)
96
+ )
97
+
98
+
99
+ register_plugin(VCard4Provider)
100
+ log = logging.getLogger(__name__)