slidge 0.1.3__py3-none-any.whl → 0.2.0__py3-none-any.whl

Sign up to get free protection for your applications and to get access to all the features.
Files changed (102) hide show
  1. slidge/__init__.py +3 -5
  2. slidge/__main__.py +2 -197
  3. slidge/__version__.py +5 -0
  4. slidge/command/adhoc.py +40 -17
  5. slidge/command/admin.py +24 -12
  6. slidge/command/base.py +10 -8
  7. slidge/command/categories.py +13 -3
  8. slidge/command/chat_command.py +29 -2
  9. slidge/command/register.py +32 -16
  10. slidge/command/user.py +106 -13
  11. slidge/contact/contact.py +254 -50
  12. slidge/contact/roster.py +124 -53
  13. slidge/core/config.py +19 -13
  14. slidge/core/dispatcher/__init__.py +3 -0
  15. slidge/core/{gateway → dispatcher}/caps.py +12 -8
  16. slidge/core/{gateway → dispatcher}/disco.py +10 -18
  17. slidge/core/dispatcher/message/__init__.py +10 -0
  18. slidge/core/dispatcher/message/chat_state.py +40 -0
  19. slidge/core/dispatcher/message/marker.py +62 -0
  20. slidge/core/dispatcher/message/message.py +397 -0
  21. slidge/core/dispatcher/muc/__init__.py +12 -0
  22. slidge/core/dispatcher/muc/admin.py +98 -0
  23. slidge/core/{gateway → dispatcher/muc}/mam.py +25 -17
  24. slidge/core/dispatcher/muc/misc.py +121 -0
  25. slidge/core/dispatcher/muc/owner.py +96 -0
  26. slidge/core/{gateway → dispatcher/muc}/ping.py +11 -17
  27. slidge/core/dispatcher/presence.py +176 -0
  28. slidge/core/dispatcher/registration.py +85 -0
  29. slidge/core/{gateway → dispatcher}/search.py +9 -16
  30. slidge/core/dispatcher/session_dispatcher.py +84 -0
  31. slidge/core/dispatcher/util.py +174 -0
  32. slidge/core/{gateway/vcard_temp.py → dispatcher/vcard.py} +35 -19
  33. slidge/core/{gateway/base.py → gateway.py} +176 -153
  34. slidge/core/mixins/__init__.py +11 -1
  35. slidge/core/mixins/attachment.py +106 -67
  36. slidge/core/mixins/avatar.py +94 -25
  37. slidge/core/mixins/base.py +10 -4
  38. slidge/core/mixins/db.py +18 -0
  39. slidge/core/mixins/disco.py +0 -10
  40. slidge/core/mixins/lock.py +10 -8
  41. slidge/core/mixins/message.py +11 -195
  42. slidge/core/mixins/message_maker.py +17 -9
  43. slidge/core/mixins/message_text.py +211 -0
  44. slidge/core/mixins/presence.py +17 -4
  45. slidge/core/pubsub.py +114 -288
  46. slidge/core/session.py +101 -40
  47. slidge/db/__init__.py +4 -0
  48. slidge/db/alembic/__init__.py +0 -0
  49. slidge/db/alembic/env.py +64 -0
  50. slidge/db/alembic/old_user_store.py +183 -0
  51. slidge/db/alembic/script.py.mako +26 -0
  52. slidge/db/alembic/versions/09f27f098baa_add_missing_attributes_in_room.py +36 -0
  53. slidge/db/alembic/versions/15b0bd83407a_remove_bogus_unique_constraints_on_room_.py +85 -0
  54. slidge/db/alembic/versions/2461390c0af2_store_contacts_caps_verstring_in_db.py +36 -0
  55. slidge/db/alembic/versions/29f5280c61aa_store_subject_setter_in_room.py +37 -0
  56. slidge/db/alembic/versions/2b1f45ab7379_store_room_subject_setter_by_nickname.py +41 -0
  57. slidge/db/alembic/versions/3071e0fa69d4_add_contact_client_type.py +52 -0
  58. slidge/db/alembic/versions/45c24cc73c91_add_bob.py +42 -0
  59. slidge/db/alembic/versions/5bd48bfdffa2_lift_room_legacy_id_constraint.py +61 -0
  60. slidge/db/alembic/versions/82a4af84b679_add_muc_history_filled.py +48 -0
  61. slidge/db/alembic/versions/8b993243a536_add_vcard_content_to_contact_table.py +43 -0
  62. slidge/db/alembic/versions/8d2ced764698_rely_on_db_to_store_contacts_rooms_and_.py +139 -0
  63. slidge/db/alembic/versions/aa9d82a7f6ef_db_creation.py +101 -0
  64. slidge/db/alembic/versions/abba1ae0edb3_store_avatar_legacy_id_in_the_contact_.py +79 -0
  65. slidge/db/alembic/versions/b33993e87db3_move_everything_to_persistent_db.py +214 -0
  66. slidge/db/alembic/versions/b64b1a793483_add_source_and_legacy_id_for_archived_.py +52 -0
  67. slidge/db/alembic/versions/c4a8ec35a0e8_per_room_user_nick.py +34 -0
  68. slidge/db/alembic/versions/e91195719c2c_store_users_avatars_persistently.py +26 -0
  69. slidge/db/avatar.py +205 -0
  70. slidge/db/meta.py +72 -0
  71. slidge/db/models.py +405 -0
  72. slidge/db/store.py +1257 -0
  73. slidge/group/archive.py +58 -14
  74. slidge/group/bookmarks.py +89 -65
  75. slidge/group/participant.py +107 -40
  76. slidge/group/room.py +402 -213
  77. slidge/main.py +202 -0
  78. slidge/migration.py +45 -1
  79. slidge/slixfix/__init__.py +31 -1
  80. slidge/{core/gateway → slixfix}/delivery_receipt.py +1 -1
  81. slidge/slixfix/roster.py +13 -4
  82. slidge/slixfix/xep_0292/vcard4.py +1 -87
  83. slidge/util/archive_msg.py +2 -1
  84. slidge/util/db.py +4 -228
  85. slidge/util/test.py +91 -4
  86. slidge/util/types.py +39 -4
  87. slidge/util/util.py +45 -2
  88. {slidge-0.1.3.dist-info → slidge-0.2.0.dist-info}/METADATA +10 -5
  89. slidge-0.2.0.dist-info/RECORD +131 -0
  90. slidge-0.2.0.dist-info/entry_points.txt +3 -0
  91. slidge/core/cache.py +0 -183
  92. slidge/core/gateway/__init__.py +0 -3
  93. slidge/core/gateway/muc_admin.py +0 -35
  94. slidge/core/gateway/presence.py +0 -95
  95. slidge/core/gateway/registration.py +0 -53
  96. slidge/core/gateway/session_dispatcher.py +0 -804
  97. slidge/util/schema.sql +0 -126
  98. slidge/util/sql.py +0 -508
  99. slidge-0.1.3.dist-info/RECORD +0 -96
  100. slidge-0.1.3.dist-info/entry_points.txt +0 -3
  101. {slidge-0.1.3.dist-info → slidge-0.2.0.dist-info}/LICENSE +0 -0
  102. {slidge-0.1.3.dist-info → slidge-0.2.0.dist-info}/WHEEL +0 -0
slidge/core/pubsub.py CHANGED
@@ -1,12 +1,8 @@
1
- import hashlib
2
- import io
3
1
  import logging
4
2
  from copy import copy
5
3
  from pathlib import Path
6
- from typing import TYPE_CHECKING, Optional, Type, Union
4
+ from typing import TYPE_CHECKING, Optional, Union
7
5
 
8
- from PIL import Image, UnidentifiedImageError
9
- from PIL.Image import Image as PILImage
10
6
  from slixmpp import (
11
7
  JID,
12
8
  CoroutineCallback,
@@ -24,34 +20,28 @@ from slixmpp.plugins.xep_0172 import UserNick
24
20
  from slixmpp.plugins.xep_0292.stanza import VCard4
25
21
  from slixmpp.types import JidStr, OptJidStr
26
22
 
27
- from ..contact.contact import LegacyContact
28
- from ..contact.roster import ContactIsUser
29
- from ..util.db import GatewayUser, user_store
30
- from ..util.sql import db
31
- from ..util.types import AvatarType, LegacyFileIdType, PepItemType
32
- from .cache import CachedAvatar, avatar_cache
23
+ from ..db.avatar import CachedAvatar, avatar_cache
24
+ from ..db.store import ContactStore, SlidgeStore
33
25
  from .mixins.lock import NamedLockMixin
34
26
 
35
27
  if TYPE_CHECKING:
36
- from slidge import BaseGateway
28
+ from slidge.core.gateway import BaseGateway
29
+
30
+ from ..contact.contact import LegacyContact
37
31
 
38
32
  VCARD4_NAMESPACE = "urn:xmpp:vcard4"
39
33
 
40
34
 
41
35
  class PepItem:
42
- @staticmethod
43
- def from_db(jid: JID, user: Optional[GatewayUser] = None) -> Optional["PepItem"]:
44
- raise NotImplementedError
45
-
46
- def to_db(self, jid: JID, user: Optional[GatewayUser] = None):
47
- raise NotImplementedError
36
+ pass
48
37
 
49
38
 
50
39
  class PepAvatar(PepItem):
51
- def __init__(self, jid: JID):
40
+ store: SlidgeStore
41
+
42
+ def __init__(self):
52
43
  self.metadata: Optional[AvatarMetadata] = None
53
44
  self.id: Optional[str] = None
54
- self.jid = jid
55
45
  self._avatar_data_path: Optional[Path] = None
56
46
 
57
47
  @property
@@ -62,63 +52,7 @@ class PepAvatar(PepItem):
62
52
  data.set_value(self._avatar_data_path.read_bytes())
63
53
  return data
64
54
 
65
- @staticmethod
66
- def _sha(b: bytes):
67
- return hashlib.sha1(b).hexdigest()
68
-
69
- def _get_weak_unique_id(self, avatar: AvatarType):
70
- if isinstance(avatar, str):
71
- return avatar # url
72
- elif isinstance(avatar, bytes):
73
- return self._sha(avatar)
74
- elif isinstance(avatar, Path):
75
- return self._sha(avatar.read_bytes())
76
-
77
- @staticmethod
78
- async def _get_image(avatar: AvatarType) -> PILImage:
79
- if isinstance(avatar, str):
80
- # async with aiohttp.ClientSession() as session:
81
- async with avatar_cache.http.get(avatar) as response:
82
- return Image.open(io.BytesIO(await response.read()))
83
- elif isinstance(avatar, bytes):
84
- return Image.open(io.BytesIO(avatar))
85
- elif isinstance(avatar, Path):
86
- return Image.open(avatar)
87
- else:
88
- raise TypeError("Avatar must be bytes, a Path or a str (URL)", avatar)
89
-
90
- async def set_avatar(
91
- self, avatar: AvatarType, unique_id: Optional[LegacyFileIdType] = None
92
- ):
93
- if unique_id is None:
94
- if isinstance(avatar, str):
95
- await self._set_avatar_from_url_alone(avatar)
96
- return
97
- unique_id = self._get_weak_unique_id(avatar)
98
-
99
- await self._set_avatar_from_unique_id(avatar, unique_id)
100
-
101
- async def _set_avatar_from_unique_id(
102
- self, avatar: AvatarType, unique_id: LegacyFileIdType
103
- ):
104
- cached_avatar = avatar_cache.get(unique_id)
105
- if cached_avatar:
106
- # this shouldn't be necessary but here to re-use avatars downloaded
107
- # before the change introducing the JID to unique ID mapping
108
- avatar_cache.store_jid(self.jid, unique_id)
109
- else:
110
- img = await self._get_image(avatar)
111
- cached_avatar = await avatar_cache.convert_and_store(
112
- img, unique_id, self.jid
113
- )
114
-
115
- self._set_avatar_from_cache(cached_avatar)
116
-
117
- async def _set_avatar_from_url_alone(self, url: str):
118
- cached_avatar = await avatar_cache.get_avatar_from_url_alone(url, self.jid)
119
- self._set_avatar_from_cache(cached_avatar)
120
-
121
- def _set_avatar_from_cache(self, cached_avatar: CachedAvatar):
55
+ def set_avatar_from_cache(self, cached_avatar: CachedAvatar):
122
56
  metadata = AvatarMetadata()
123
57
  self.id = cached_avatar.hash
124
58
  metadata.add_info(
@@ -131,31 +65,10 @@ class PepAvatar(PepItem):
131
65
  self.metadata = metadata
132
66
  self._avatar_data_path = cached_avatar.path
133
67
 
134
- @staticmethod
135
- def from_db(jid: JID, user: Optional[GatewayUser] = None) -> Optional["PepAvatar"]:
136
- cached_id = db.avatar_get(jid)
137
- if cached_id is None:
138
- return None
139
- item = PepAvatar(jid)
140
- cached_avatar = avatar_cache.get(cached_id)
141
- if cached_avatar is None:
142
- raise XMPPError("internal-server-error")
143
- item._set_avatar_from_cache(cached_avatar)
144
- return item
145
-
146
- def to_db(self, jid: JID, user=None):
147
- cached_id = avatar_cache.get_cached_id_for(jid)
148
- if cached_id is None:
149
- log.warning("Could not store avatar for %s", jid)
150
- return
151
- db.avatar_store(jid, cached_id)
152
-
153
- @staticmethod
154
- def remove_from_db(jid: JID):
155
- db.avatar_delete(jid)
156
-
157
68
 
158
69
  class PepNick(PepItem):
70
+ contact_store: ContactStore
71
+
159
72
  def __init__(self, nick: Optional[str] = None):
160
73
  nickname = UserNick()
161
74
  if nick is not None:
@@ -163,20 +76,6 @@ class PepNick(PepItem):
163
76
  self.nick = nickname
164
77
  self.__nick_str = nick
165
78
 
166
- @staticmethod
167
- def from_db(jid: JID, user: Optional[GatewayUser] = None) -> Optional["PepNick"]:
168
- if user is None:
169
- raise XMPPError("not-allowed")
170
- nick = db.nick_get(jid, user)
171
- if nick is None:
172
- return None
173
- return PepNick(nick)
174
-
175
- def to_db(self, jid: JID, user: Optional[GatewayUser] = None):
176
- if user is None:
177
- raise XMPPError("not-allowed")
178
- db.nick_store(jid, str(self.__nick_str), user)
179
-
180
79
 
181
80
  class PubSubComponent(NamedLockMixin, BasePlugin):
182
81
  xmpp: "BaseGateway"
@@ -186,7 +85,6 @@ class PubSubComponent(NamedLockMixin, BasePlugin):
186
85
  dependencies = {
187
86
  "xep_0030",
188
87
  "xep_0060",
189
- # "xep_0084",
190
88
  "xep_0115",
191
89
  "xep_0163",
192
90
  }
@@ -219,7 +117,6 @@ class PubSubComponent(NamedLockMixin, BasePlugin):
219
117
  self._get_vcard, # type:ignore
220
118
  )
221
119
  )
222
- self.xmpp.add_event_handler("presence_available", self._on_presence_available)
223
120
 
224
121
  disco = self.xmpp.plugin["xep_0030"]
225
122
  disco.add_identity("pubsub", "pep", self.component_name)
@@ -228,82 +125,72 @@ class PubSubComponent(NamedLockMixin, BasePlugin):
228
125
  disco.add_feature("http://jabber.org/protocol/pubsub#retrieve-items")
229
126
  disco.add_feature("http://jabber.org/protocol/pubsub#persistent-items")
230
127
 
231
- async def _on_presence_available(self, p: Presence):
128
+ async def __get_features(self, presence: Presence) -> list[str]:
129
+ from_ = presence.get_from()
130
+ ver_string = presence["caps"]["ver"]
131
+ if ver_string:
132
+ info = await self.xmpp.plugin["xep_0115"].get_caps(from_)
133
+ else:
134
+ info = None
135
+ if info is None:
136
+ async with self.lock(from_):
137
+ iq = await self.xmpp.plugin["xep_0030"].get_info(from_)
138
+ info = iq["disco_info"]
139
+ return info["features"]
140
+
141
+ async def on_presence_available(
142
+ self, p: Presence, contact: Optional["LegacyContact"]
143
+ ):
232
144
  if p.get_plugin("muc_join", check=True) is not None:
233
145
  log.debug("Ignoring MUC presence here")
234
146
  return
235
147
 
236
- from_ = p.get_from()
237
- ver_string = p["caps"]["ver"]
238
- info = None
239
-
240
148
  to = p.get_to()
241
-
242
- # we don't want to push anything for contacts that are not in the user's roster
243
149
  if to != self.xmpp.boundjid.bare:
244
- session = self.xmpp.get_session_from_stanza(p)
245
-
246
- if session is None:
150
+ # we don't want to push anything for contacts that are not in the user's roster
151
+ if contact is None or not contact.is_friend:
247
152
  return
248
153
 
249
- await session.contacts.ready
250
- try:
251
- contact = await session.contacts.by_jid(to)
252
- except XMPPError as e:
253
- log.debug(
254
- "Could not determine if %s was added to the roster: %s", to, e
255
- )
256
- return
257
- except Exception as e:
258
- log.warning("Could not determine if %s was added to the roster.", to)
259
- log.exception(e)
260
- return
261
- if not contact.is_friend:
262
- return
154
+ from_ = p.get_from()
155
+ features = await self.__get_features(p)
263
156
 
264
- if ver_string:
265
- info = await self.xmpp.plugin["xep_0115"].get_caps(from_)
266
- if info is None:
267
- async with self.lock(from_):
268
- iq = await self.xmpp.plugin["xep_0030"].get_info(from_)
269
- info = iq["disco_info"]
270
- features = info["features"]
271
157
  if AvatarMetadata.namespace + "+notify" in features:
272
158
  try:
273
- pep_avatar = await self._get_authorized_avatar(p)
159
+ pep_avatar = await self._get_authorized_avatar(p, contact)
274
160
  except XMPPError:
275
161
  pass
276
162
  else:
277
- if pep_avatar.metadata is None:
278
- raise XMPPError("internal-server-error", "Avatar but no metadata?")
279
- await self._broadcast(
280
- data=pep_avatar.metadata,
281
- from_=p.get_to(),
282
- to=from_,
283
- id=pep_avatar.metadata["info"]["id"],
284
- )
163
+ if pep_avatar.metadata is not None:
164
+ await self.__broadcast(
165
+ data=pep_avatar.metadata,
166
+ from_=p.get_to().bare,
167
+ to=from_,
168
+ id=pep_avatar.metadata["info"]["id"],
169
+ )
285
170
  if UserNick.namespace + "+notify" in features:
286
171
  try:
287
- pep_nick = await self._get_authorized_nick(p)
172
+ pep_nick = await self._get_authorized_nick(p, contact)
288
173
  except XMPPError:
289
174
  pass
290
175
  else:
291
- await self._broadcast(data=pep_nick.nick, from_=p.get_to(), to=from_)
176
+ await self.__broadcast(data=pep_nick.nick, from_=p.get_to(), to=from_)
292
177
 
293
- if VCARD4_NAMESPACE + "+notify" in features:
294
- await self.broadcast_vcard_event(p.get_to(), to=from_)
178
+ if contact is not None and VCARD4_NAMESPACE + "+notify" in features:
179
+ await self.broadcast_vcard_event(
180
+ p.get_to(), from_, await contact.get_vcard()
181
+ )
295
182
 
296
- async def broadcast_vcard_event(self, from_, to):
183
+ async def broadcast_vcard_event(self, from_: JID, to: JID, vcard: VCard4 | None):
297
184
  item = Item()
298
185
  item.namespace = VCARD4_NAMESPACE
299
186
  item["id"] = "current"
300
- vcard: VCard4 = await self.xmpp["xep_0292_provider"].get_vcard(from_, to)
187
+ # vcard: VCard4 = await self.xmpp["xep_0292_provider"].get_vcard(from_, to)
301
188
  # The vcard content should NOT be in this event according to the spec:
302
189
  # https://xmpp.org/extensions/xep-0292.html#sect-idm45669698174224
303
- # but movim expects it to be here, and I guess
190
+ # but movim expects it to be here, and I guess it does not hurt
304
191
 
305
192
  log.debug("Broadcast vcard4 event: %s", vcard)
306
- await self._broadcast(
193
+ await self.__broadcast(
307
194
  data=vcard,
308
195
  from_=JID(from_).bare,
309
196
  to=to,
@@ -311,71 +198,76 @@ class PubSubComponent(NamedLockMixin, BasePlugin):
311
198
  node=VCARD4_NAMESPACE,
312
199
  )
313
200
 
314
- async def _get_authorized_item(
315
- self, cls: Type[PepItemType], stanza: Union[Iq, Presence]
316
- ) -> PepItemType:
317
- sto = stanza.get_to()
318
- user = user_store.get_by_jid(stanza.get_from())
319
- item = cls.from_db(sto, user)
320
- if item is None:
321
- raise XMPPError("item-not-found")
322
-
323
- if sto != self.xmpp.boundjid.bare:
324
- session = self.xmpp.get_session_from_stanza(stanza)
325
- await session.contacts.by_jid(sto)
326
-
327
- return item # type:ignore
201
+ async def __get_contact(self, stanza: Union[Iq, Presence]):
202
+ session = self.xmpp.get_session_from_stanza(stanza)
203
+ return await session.contacts.by_jid(stanza.get_to())
204
+
205
+ async def _get_authorized_avatar(
206
+ self, stanza: Union[Iq, Presence], contact: Optional["LegacyContact"] = None
207
+ ) -> PepAvatar:
208
+ if stanza.get_to() == self.xmpp.boundjid.bare:
209
+ item = PepAvatar()
210
+ item.set_avatar_from_cache(avatar_cache.get_by_pk(self.xmpp.avatar_pk))
211
+ return item
212
+
213
+ if contact is None:
214
+ contact = await self.__get_contact(stanza)
215
+
216
+ item = PepAvatar()
217
+ if contact.avatar_pk is not None:
218
+ stored = avatar_cache.get_by_pk(contact.avatar_pk)
219
+ assert stored is not None
220
+ item.set_avatar_from_cache(stored)
221
+ return item
328
222
 
329
- async def _get_authorized_avatar(self, stanza: Union[Iq, Presence]) -> PepAvatar:
330
- return await self._get_authorized_item(PepAvatar, stanza)
223
+ async def _get_authorized_nick(
224
+ self, stanza: Union[Iq, Presence], contact: Optional["LegacyContact"] = None
225
+ ) -> PepNick:
226
+ if stanza.get_to() == self.xmpp.boundjid.bare:
227
+ return PepNick(self.xmpp.COMPONENT_NAME)
331
228
 
332
- async def _get_authorized_nick(self, stanza: Union[Iq, Presence]) -> PepNick:
333
- return await self._get_authorized_item(PepNick, stanza)
229
+ if contact is None:
230
+ contact = await self.__get_contact(stanza)
334
231
 
335
- async def _get_avatar_data(self, iq: Iq):
336
- pep_avatar = await self._get_authorized_avatar(iq)
232
+ if contact.name is not None:
233
+ return PepNick(contact.name)
234
+ else:
235
+ return PepNick()
337
236
 
237
+ def __reply_with(
238
+ self, iq: Iq, content: AvatarData | AvatarMetadata | None, item_id: str | None
239
+ ) -> None:
338
240
  requested_items = iq["pubsub"]["items"]
241
+
339
242
  if len(requested_items) == 0:
340
- self._reply_with_payload(iq, pep_avatar.data, pep_avatar.id)
243
+ self._reply_with_payload(iq, content, item_id)
341
244
  else:
342
245
  for item in requested_items:
343
- if item["id"] == pep_avatar.id:
344
- self._reply_with_payload(iq, pep_avatar.data, pep_avatar.id)
246
+ if item["id"] == item_id:
247
+ self._reply_with_payload(iq, content, item_id)
345
248
  return
346
249
  else:
347
250
  raise XMPPError("item-not-found")
348
251
 
349
- async def _get_avatar_metadata(self, iq: Iq):
252
+ async def _get_avatar_data(self, iq: Iq):
350
253
  pep_avatar = await self._get_authorized_avatar(iq)
254
+ self.__reply_with(iq, pep_avatar.data, pep_avatar.id)
351
255
 
352
- requested_items = iq["pubsub"]["items"]
353
- if len(requested_items) == 0:
354
- self._reply_with_payload(iq, pep_avatar.metadata, pep_avatar.id)
355
- else:
356
- for item in requested_items:
357
- if item["id"] == pep_avatar.id:
358
- self._reply_with_payload(iq, pep_avatar.metadata, pep_avatar.id)
359
- return
360
- else:
361
- raise XMPPError("item-not-found")
256
+ async def _get_avatar_metadata(self, iq: Iq):
257
+ pep_avatar = await self._get_authorized_avatar(iq)
258
+ self.__reply_with(iq, pep_avatar.metadata, pep_avatar.id)
362
259
 
363
260
  async def _get_vcard(self, iq: Iq):
364
261
  # this is not the proper way that clients should retrieve VCards, but
365
262
  # gajim does it this way.
366
263
  # https://xmpp.org/extensions/xep-0292.html#sect-idm45669698174224
367
- vcard: VCard4 = await self.xmpp["xep_0292_provider"].get_vcard(
368
- iq.get_to().bare, iq.get_from().bare
369
- )
370
- log.debug("VCARD: %s -- %s -- %s", iq.get_to().bare, iq.get_from().bare, vcard)
264
+ session = self.xmpp.get_session_from_stanza(iq)
265
+ contact = await session.contacts.by_jid(iq.get_to())
266
+ vcard = await contact.get_vcard()
371
267
  if vcard is None:
372
268
  raise XMPPError("item-not-found")
373
269
  self._reply_with_payload(iq, vcard, "current", VCARD4_NAMESPACE)
374
270
 
375
- @staticmethod
376
- def get_avatar(jid: JID):
377
- return PepAvatar.from_db(jid)
378
-
379
271
  @staticmethod
380
272
  def _reply_with_payload(
381
273
  iq: Iq,
@@ -394,7 +286,7 @@ class PubSubComponent(NamedLockMixin, BasePlugin):
394
286
  result["pubsub"]["items"].append(item)
395
287
  result.send()
396
288
 
397
- async def _broadcast(self, data, from_: JidStr, to: OptJidStr = None, **kwargs):
289
+ async def __broadcast(self, data, from_: JidStr, to: OptJidStr = None, **kwargs):
398
290
  from_ = JID(from_)
399
291
  if from_ != self.xmpp.boundjid.bare and to is not None:
400
292
  to = JID(to)
@@ -402,12 +294,6 @@ class PubSubComponent(NamedLockMixin, BasePlugin):
402
294
  if session is None:
403
295
  return
404
296
  await session.ready
405
- try:
406
- entity = await session.get_contact_or_group_or_participant(from_)
407
- except ContactIsUser:
408
- return
409
- if isinstance(entity, LegacyContact) and not entity.is_friend:
410
- return
411
297
 
412
298
  item = EventItem()
413
299
  if data:
@@ -428,97 +314,37 @@ class PubSubComponent(NamedLockMixin, BasePlugin):
428
314
  msg.append(event)
429
315
 
430
316
  if to is None:
431
- for u in user_store.get_all():
317
+ for u in self.xmpp.store.users.get_all():
432
318
  new_msg = copy(msg)
433
- new_msg.set_to(u.bare_jid)
319
+ new_msg.set_to(u.jid.bare)
434
320
  new_msg.send()
435
321
  else:
436
322
  msg.set_to(to)
437
323
  msg.send()
438
324
 
439
- async def set_avatar(
440
- self,
441
- jid: JidStr,
442
- avatar: Optional[AvatarType] = None,
443
- broadcast_to: OptJidStr = None,
444
- unique_id=None,
445
- broadcast=True,
446
- ):
447
- jid = JID(jid)
448
- if avatar is None:
449
- PepAvatar.remove_from_db(jid)
450
- await self._broadcast(AvatarMetadata(), jid, broadcast_to)
451
- avatar_cache.delete_jid(jid)
452
- else:
453
- pep_avatar = PepAvatar(jid)
454
- try:
455
- await pep_avatar.set_avatar(avatar, unique_id)
456
- except (UnidentifiedImageError, FileNotFoundError) as e:
457
- log.warning("Failed to set avatar for %s: %r", self, e)
458
- return
459
- pep_avatar.to_db(jid)
460
- if pep_avatar.metadata is None:
461
- raise RuntimeError
462
- if not broadcast:
463
- return
464
- await self._broadcast(
465
- pep_avatar.metadata,
466
- jid,
467
- broadcast_to,
468
- id=pep_avatar.metadata["info"]["id"],
469
- )
470
-
471
- async def set_avatar_from_cache(
472
- self, jid: JID, send_empty: bool, broadcast_to: OptJidStr = None, broadcast=True
473
- ):
474
- uid = avatar_cache.get_cached_id_for(jid)
475
- if uid is None:
476
- if not send_empty:
477
- return
478
- self.xmpp.loop.create_task(
479
- self.set_avatar(jid, None, broadcast_to, uid, broadcast)
480
- )
481
- return
482
- cached_avatar = avatar_cache.get(str(uid))
325
+ async def broadcast_avatar(
326
+ self, from_: JidStr, to: JidStr, cached_avatar: Optional[CachedAvatar]
327
+ ) -> None:
483
328
  if cached_avatar is None:
484
- # should not happen but well…
485
- log.warning(
486
- "Something is wrong with the avatar, %s won't have an "
487
- "avatar because avatar not found in cache",
488
- jid,
329
+ await self.__broadcast(AvatarMetadata(), from_, to)
330
+ else:
331
+ pep_avatar = PepAvatar()
332
+ pep_avatar.set_avatar_from_cache(cached_avatar)
333
+ assert pep_avatar.metadata is not None
334
+ await self.__broadcast(
335
+ pep_avatar.metadata, from_, to, id=pep_avatar.metadata["info"]["id"]
489
336
  )
490
- return
491
- self.xmpp.loop.create_task(
492
- self.set_avatar(jid, cached_avatar.path, broadcast_to, uid, broadcast)
493
- )
494
337
 
495
- def set_nick(
338
+ def broadcast_nick(
496
339
  self,
497
- user: GatewayUser,
340
+ user_jid: JID,
498
341
  jid: JidStr,
499
342
  nick: Optional[str] = None,
500
343
  ):
501
344
  jid = JID(jid)
502
345
  nickname = PepNick(nick)
503
- nickname.to_db(jid, user)
504
346
  log.debug("New nickname: %s", nickname.nick)
505
- self.xmpp.loop.create_task(self._broadcast(nickname.nick, jid, user.bare_jid))
506
-
507
- async def broadcast_all(self, from_: JID, to: JID):
508
- """
509
- Force push avatar and nick for a stored JID.
510
- """
511
- a = PepAvatar.from_db(from_)
512
- if a:
513
- if a.metadata:
514
- await self._broadcast(
515
- a.metadata, from_, to, id=a.metadata["info"]["id"]
516
- )
517
- else:
518
- log.warning("No metadata associated to this cached avatar?!")
519
- n = PepNick.from_db(from_, user_store.get_by_jid(to))
520
- if n:
521
- await self._broadcast(n.nick, from_, to)
347
+ self.xmpp.loop.create_task(self.__broadcast(nickname.nick, jid, user_jid.bare))
522
348
 
523
349
 
524
350
  log = logging.getLogger(__name__)