slidge 0.1.0b2__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 (155) hide show
  1. slidge/__init__.py +55 -31
  2. slidge/__main__.py +118 -116
  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 +183 -0
  16. slidge/core/config.py +216 -0
  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 +19 -0
  31. slidge/core/mixins/attachment.py +506 -0
  32. slidge/core/mixins/avatar.py +167 -0
  33. slidge/core/mixins/base.py +31 -0
  34. slidge/core/mixins/disco.py +130 -0
  35. slidge/core/mixins/lock.py +31 -0
  36. slidge/core/mixins/message.py +398 -0
  37. slidge/core/mixins/message_maker.py +154 -0
  38. slidge/core/mixins/presence.py +217 -0
  39. slidge/core/mixins/recipient.py +43 -0
  40. slidge/core/pubsub.py +282 -116
  41. slidge/core/session.py +595 -372
  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_0084 → slixfix/link_preview}/__init__.py +3 -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 +14 -2
  54. slidge/slixfix/xep_0077/stanza.py +104 -0
  55. slidge/{util → slixfix}/xep_0100/gateway.py +25 -15
  56. slidge/slixfix/xep_0100/stanza.py +9 -0
  57. slidge/slixfix/xep_0153/__init__.py +10 -0
  58. slidge/slixfix/xep_0153/stanza.py +25 -0
  59. slidge/slixfix/xep_0153/vcard_avatar.py +23 -0
  60. slidge/slixfix/xep_0264/__init__.py +5 -0
  61. slidge/slixfix/xep_0264/stanza.py +36 -0
  62. slidge/slixfix/xep_0264/thumbnail.py +23 -0
  63. slidge/slixfix/xep_0292/__init__.py +5 -0
  64. slidge/slixfix/xep_0292/vcard4.py +100 -0
  65. slidge/slixfix/xep_0313/__init__.py +12 -0
  66. slidge/slixfix/xep_0313/mam.py +262 -0
  67. slidge/slixfix/xep_0313/stanza.py +359 -0
  68. slidge/slixfix/xep_0317/__init__.py +5 -0
  69. slidge/slixfix/xep_0317/hats.py +17 -0
  70. slidge/slixfix/xep_0317/stanza.py +28 -0
  71. slidge/{util → slixfix}/xep_0356_old/privilege.py +9 -7
  72. slidge/slixfix/xep_0424/__init__.py +9 -0
  73. slidge/slixfix/xep_0424/retraction.py +77 -0
  74. slidge/slixfix/xep_0424/stanza.py +28 -0
  75. slidge/slixfix/xep_0490/__init__.py +8 -0
  76. slidge/slixfix/xep_0490/mds.py +47 -0
  77. slidge/slixfix/xep_0490/stanza.py +17 -0
  78. slidge/util/__init__.py +4 -6
  79. slidge/util/archive_msg.py +61 -0
  80. slidge/util/conf.py +206 -0
  81. slidge/util/db.py +57 -76
  82. slidge/util/schema.sql +126 -0
  83. slidge/util/sql.py +508 -0
  84. slidge/util/test.py +215 -25
  85. slidge/util/types.py +177 -4
  86. slidge/util/util.py +225 -59
  87. slidge-0.1.1.dist-info/METADATA +110 -0
  88. slidge-0.1.1.dist-info/RECORD +96 -0
  89. {slidge-0.1.0b2.dist-info → slidge-0.1.1.dist-info}/WHEEL +1 -1
  90. slidge/core/contact.py +0 -891
  91. slidge/core/gateway.py +0 -916
  92. slidge/plugins/discord/__init__.py +0 -90
  93. slidge/plugins/discord/client.py +0 -108
  94. slidge/plugins/discord/session.py +0 -162
  95. slidge/plugins/dummy.py +0 -203
  96. slidge/plugins/facebook.py +0 -493
  97. slidge/plugins/hackernews.py +0 -213
  98. slidge/plugins/mattermost/__init__.py +0 -1
  99. slidge/plugins/mattermost/api.py +0 -280
  100. slidge/plugins/mattermost/gateway.py +0 -365
  101. slidge/plugins/mattermost/websocket.py +0 -252
  102. slidge/plugins/signal/__init__.py +0 -3
  103. slidge/plugins/signal/contact.py +0 -106
  104. slidge/plugins/signal/gateway.py +0 -282
  105. slidge/plugins/signal/session.py +0 -448
  106. slidge/plugins/signal/txt.py +0 -53
  107. slidge/plugins/skype.py +0 -325
  108. slidge/plugins/steam.py +0 -310
  109. slidge/plugins/telegram/__init__.py +0 -5
  110. slidge/plugins/telegram/client.py +0 -228
  111. slidge/plugins/telegram/config.py +0 -12
  112. slidge/plugins/telegram/contact.py +0 -176
  113. slidge/plugins/telegram/gateway.py +0 -150
  114. slidge/plugins/telegram/session.py +0 -256
  115. slidge/util/xep_0030/__init__.py +0 -13
  116. slidge/util/xep_0030/disco.py +0 -811
  117. slidge/util/xep_0030/stanza/__init__.py +0 -7
  118. slidge/util/xep_0030/stanza/info.py +0 -270
  119. slidge/util/xep_0030/stanza/items.py +0 -147
  120. slidge/util/xep_0030/static.py +0 -467
  121. slidge/util/xep_0055/__init__.py +0 -5
  122. slidge/util/xep_0055/search.py +0 -75
  123. slidge/util/xep_0055/stanza.py +0 -10
  124. slidge/util/xep_0077/stanza.py +0 -71
  125. slidge/util/xep_0084/avatar.py +0 -137
  126. slidge/util/xep_0084/stanza.py +0 -104
  127. slidge/util/xep_0115/__init__.py +0 -12
  128. slidge/util/xep_0115/caps.py +0 -379
  129. slidge/util/xep_0115/stanza.py +0 -16
  130. slidge/util/xep_0115/static.py +0 -137
  131. slidge/util/xep_0292/__init__.py +0 -1
  132. slidge/util/xep_0292/stanza.py +0 -167
  133. slidge/util/xep_0292/vcard4.py +0 -75
  134. slidge/util/xep_0333/__init__.py +0 -10
  135. slidge/util/xep_0333/markers.py +0 -96
  136. slidge/util/xep_0333/stanza.py +0 -34
  137. slidge/util/xep_0356/__init__.py +0 -7
  138. slidge/util/xep_0356/permissions.py +0 -35
  139. slidge/util/xep_0356/privilege.py +0 -160
  140. slidge/util/xep_0356/stanza.py +0 -44
  141. slidge/util/xep_0363/__init__.py +0 -16
  142. slidge/util/xep_0363/http_upload.py +0 -215
  143. slidge/util/xep_0363/stanza.py +0 -46
  144. slidge/util/xep_0461/__init__.py +0 -6
  145. slidge/util/xep_0461/reply.py +0 -48
  146. slidge/util/xep_0461/stanza.py +0 -47
  147. slidge-0.1.0b2.dist-info/METADATA +0 -171
  148. slidge-0.1.0b2.dist-info/RECORD +0 -81
  149. /slidge/{plugins/__init__.py → py.typed} +0 -0
  150. /slidge/{util → slixfix}/xep_0077/__init__.py +0 -0
  151. /slidge/{util → slixfix}/xep_0100/__init__.py +0 -0
  152. /slidge/{util → slixfix}/xep_0356_old/__init__.py +0 -0
  153. /slidge/{util → slixfix}/xep_0356_old/stanza.py +0 -0
  154. {slidge-0.1.0b2.dist-info → slidge-0.1.1.dist-info}/LICENSE +0 -0
  155. {slidge-0.1.0b2.dist-info → slidge-0.1.1.dist-info}/entry_points.txt +0 -0
@@ -1,137 +0,0 @@
1
- """
2
- Slixmpp: The Slick XMPP Library
3
- Copyright (C) 2012 Nathanael C. Fritz, Lance J.T. Stout
4
- This file is part of Slixmpp.
5
-
6
- See the file LICENSE for copying permission.
7
- """
8
-
9
- from __future__ import annotations
10
-
11
- import hashlib
12
- import logging
13
- from asyncio import Future
14
- from typing import TYPE_CHECKING, Dict, Iterable, List, Optional, Set, Union
15
-
16
- from slixmpp.plugins import BasePlugin
17
- from slixmpp.stanza import Iq
18
- from slixmpp.xmlstream import JID, register_stanza_plugin
19
- from slixmpp.xmlstream.handler import Callback
20
- from slixmpp.xmlstream.matcher import StanzaPath
21
-
22
- from . import stanza
23
- from .stanza import Data, MetaData, Pointer
24
-
25
- try:
26
- from typing import TypedDict
27
- except ImportError:
28
- from typing_extensions import TypedDict
29
-
30
-
31
- class AvatarMetadataItem(TypedDict, total=False):
32
- bytes: int
33
- id: str
34
- type: str
35
- height: int
36
- width: int
37
- url: str
38
-
39
- MetadataItems = Union[
40
- AvatarMetadataItem,
41
- List[AvatarMetadataItem],
42
- Set[AvatarMetadataItem]
43
- ]
44
-
45
-
46
- log = logging.getLogger(__name__)
47
-
48
-
49
- class XEP_0084(BasePlugin):
50
-
51
- name = 'xep_0084'
52
- description = 'XEP-0084: User Avatar (Slidge)'
53
- dependencies = {'xep_0163', 'xep_0060'}
54
- stanza = stanza
55
-
56
- def plugin_init(self):
57
- pubsub_stanza = self.xmpp['xep_0060'].stanza
58
- register_stanza_plugin(pubsub_stanza.Item, Data)
59
- register_stanza_plugin(pubsub_stanza.EventItem, Data)
60
-
61
- self.xmpp['xep_0060'].map_node_event(Data.namespace, 'avatar_data')
62
-
63
- def plugin_end(self):
64
- self.xmpp['xep_0030'].del_feature(feature=MetaData.namespace)
65
- self.xmpp['xep_0163'].remove_interest(MetaData.namespace)
66
-
67
- def session_bind(self, jid):
68
- self.xmpp['xep_0163'].register_pep('avatar_metadata', MetaData)
69
-
70
- def generate_id(self, data) -> str:
71
- return hashlib.sha1(data).hexdigest()
72
-
73
- def retrieve_avatar(self, jid: JID, id: str, **pubsubkwargs) -> Future:
74
- """Retrieve an avatar.
75
-
76
- :param jid: JID of the entity to get the avatar from.
77
- :param id: Identifier of the item containing the avatar.
78
- """
79
- return self.xmpp['xep_0060'].get_item(
80
- jid,
81
- Data.namespace,
82
- id,
83
- **pubsubkwargs
84
- )
85
-
86
- def publish_avatar(self, data: bytes, **pubsubkwargs) -> Future:
87
- """Publish an avatar.
88
-
89
- :param data: The avatar, in bytes representation.
90
- """
91
- payload = Data()
92
- payload['value'] = data
93
- return self.xmpp['xep_0163'].publish(
94
- payload,
95
- id=self.generate_id(data),
96
- **pubsubkwargs
97
- )
98
-
99
- def publish_avatar_metadata(self, items: Optional[MetadataItems] = None,
100
- pointers: Optional[Iterable[Pointer]] = None,
101
- **pubsubkwargs) -> Future:
102
- """Publish avatar metadata.
103
-
104
- :param items: Metadata items to store
105
- :param pointers: Optional pointers
106
- """
107
- metadata = MetaData()
108
- if items is None:
109
- items = []
110
- if not isinstance(items, (list, set)):
111
- items = [items]
112
- for info in items:
113
- metadata.add_info(info['id'], info['type'], info['bytes'],
114
- height=info.get('height', ''),
115
- width=info.get('width', ''),
116
- url=info.get('url', ''))
117
-
118
- if pointers is not None:
119
- for pointer in pointers:
120
- metadata.add_pointer(pointer)
121
-
122
- return self.xmpp['xep_0163'].publish(
123
- metadata,
124
- id=info['id'],
125
- **pubsubkwargs
126
- )
127
-
128
- def stop(self, **pubsubkwargs) -> Future:
129
- """
130
- Clear existing avatar metadata information to stop notifications.
131
- """
132
- metadata = MetaData()
133
- return self.xmpp['xep_0163'].publish(
134
- metadata,
135
- node=MetaData.namespace,
136
- **pubsubkwargs
137
- )
@@ -1,104 +0,0 @@
1
-
2
- # Slixmpp: The Slick XMPP Library
3
- # Copyright (C) 2012 Nathanael C. Fritz, Lance J.T. Stout
4
- # This file is part of Slixmpp.
5
- # See the file LICENSE for copying permission.
6
- from base64 import b64decode, b64encode
7
-
8
- from slixmpp.util import bytes
9
- from slixmpp.xmlstream import ET, ElementBase, register_stanza_plugin
10
-
11
-
12
- class Data(ElementBase):
13
- name = 'data'
14
- namespace = 'urn:xmpp:avatar:data'
15
- plugin_attrib = 'avatar_data'
16
- interfaces = {'value'}
17
-
18
- def get_value(self):
19
- if self.xml.text:
20
- return b64decode(bytes(self.xml.text))
21
- return b''
22
-
23
- def set_value(self, value):
24
- if value:
25
- self.xml.text = b64encode(bytes(value)).decode()
26
- else:
27
- self.xml.text = ''
28
-
29
- def del_value(self):
30
- self.xml.text = ''
31
-
32
-
33
- class MetaData(ElementBase):
34
- name = 'metadata'
35
- namespace = 'urn:xmpp:avatar:metadata'
36
- plugin_attrib = 'avatar_metadata'
37
- interfaces = set()
38
-
39
- def add_info(self, id, itype, ibytes, height=None, width=None, url=None):
40
- info = Info()
41
- info.values = {'id': id,
42
- 'type': itype,
43
- 'bytes': '%s' % ibytes,
44
- 'height': height,
45
- 'width': width,
46
- 'url': url}
47
- self.append(info)
48
-
49
- def add_pointer(self, xml):
50
- if not isinstance(xml, Pointer):
51
- pointer = Pointer()
52
- pointer.append(xml)
53
- self.append(pointer)
54
- else:
55
- self.append(xml)
56
-
57
-
58
- class Info(ElementBase):
59
- name = 'info'
60
- namespace = 'urn:xmpp:avatar:metadata'
61
- plugin_attrib = 'info'
62
- plugin_multi_attrib = 'items'
63
- interfaces = {'bytes', 'height', 'id', 'type', 'url', 'width'}
64
-
65
- def _get_int(self, name: str) -> int:
66
- try:
67
- return int(self._get_attr(name))
68
- except ValueError:
69
- return 0
70
-
71
- def _set_int(self, name: str, value: int):
72
- if value not in ('', None):
73
- int(value)
74
- self._set_attr(name, value)
75
-
76
- def get_bytes(self) -> int:
77
- return self._get_int('bytes')
78
-
79
- def _set_bytes(self, value: int):
80
- self._set_int('bytes', value)
81
-
82
- def get_height(self) -> int:
83
- return self._get_int('height')
84
-
85
- def set_height(self, value: int):
86
- self._set_int('height', value)
87
-
88
- def get_width(self) -> int:
89
- return self._get_int('width')
90
-
91
- def set_width(self, value: int):
92
- self._set_int('width', value)
93
-
94
-
95
- class Pointer(ElementBase):
96
- name = 'pointer'
97
- namespace = 'urn:xmpp:avatar:metadata'
98
- plugin_attrib = 'pointer'
99
- plugin_multi_attrib = 'pointers'
100
- interfaces = set()
101
-
102
-
103
- register_stanza_plugin(MetaData, Info, iterable=True)
104
- register_stanza_plugin(MetaData, Pointer, iterable=True)
@@ -1,12 +0,0 @@
1
- # isort: skip_file
2
- # Slixmpp: The Slick XMPP Library
3
- # Copyright (C) 2011 Nathanael C. Fritz, Lance J.T. Stout
4
- # This file is part of Slixmpp.
5
- # See the file LICENSE for copying permission.
6
- from slixmpp.plugins.base import register_plugin
7
-
8
- from .stanza import Capabilities
9
- from .static import StaticCaps
10
- from .caps import XEP_0115
11
-
12
- register_plugin(XEP_0115)
@@ -1,379 +0,0 @@
1
- # Slixmpp: The Slick XMPP Library
2
- # Copyright (C) 2011 Nathanael C. Fritz, Lance J.T. Stout
3
- # This file is part of Slixmpp.
4
- # See the file LICENSE for copying permission.
5
- import base64
6
- import hashlib
7
- import logging
8
- from asyncio import Future, Lock
9
- from typing import Optional
10
-
11
- from slixmpp import __version__
12
- from slixmpp.exceptions import XMPPError
13
- from slixmpp.plugins import BasePlugin
14
- from slixmpp.stanza import Iq, Presence, StreamFeatures
15
- from slixmpp.types import OptJidStr
16
- from slixmpp.util import MemoryCache
17
- from slixmpp.xmlstream import JID, register_stanza_plugin
18
- from slixmpp.xmlstream.handler import Callback
19
- from slixmpp.xmlstream.matcher import StanzaPath
20
-
21
- from . import StaticCaps, stanza
22
-
23
- log = logging.getLogger(__name__)
24
-
25
-
26
- class XEP_0115(BasePlugin):
27
-
28
- """
29
- XEP-0115: Entity Capabilities
30
- """
31
-
32
- name = "xep_0115"
33
- description = "XEP-0115: Entity Capabilities"
34
- dependencies = {"xep_0030", "xep_0128", "xep_0004"}
35
- stanza = stanza
36
- default_config = {
37
- "hash": "sha-1",
38
- "caps_node": None,
39
- "broadcast": True,
40
- "cache": None,
41
- }
42
- lock = Lock()
43
-
44
- def plugin_init(self):
45
- self.hashes = {"sha-1": hashlib.sha1, "sha1": hashlib.sha1, "md5": hashlib.md5}
46
-
47
- if self.caps_node is None:
48
- self.caps_node = "http://slixmpp.com/ver/%s" % __version__
49
-
50
- if self.cache is None:
51
- self.cache = MemoryCache()
52
-
53
- register_stanza_plugin(Presence, stanza.Capabilities)
54
- register_stanza_plugin(StreamFeatures, stanza.Capabilities)
55
-
56
- self._disco_ops = [
57
- "cache_caps",
58
- "get_caps",
59
- "assign_verstring",
60
- "get_verstring",
61
- "supports",
62
- "has_identity",
63
- ]
64
-
65
- self.xmpp.register_handler(
66
- Callback(
67
- "Entity Capabilites", StanzaPath("presence/caps"), self._handle_caps
68
- )
69
- )
70
-
71
- self.xmpp.add_filter("out", self._filter_add_caps)
72
-
73
- self.xmpp.add_event_handler("entity_caps", self._process_caps)
74
-
75
- if not self.xmpp.is_component:
76
- self.xmpp.register_feature(
77
- "caps", self._handle_caps_feature, restart=False, order=10010
78
- )
79
-
80
- disco = self.xmpp["xep_0030"]
81
- self.static = StaticCaps(self.xmpp, disco.static)
82
-
83
- for op in self._disco_ops:
84
- self.api.register(getattr(self.static, op), op, default=True)
85
-
86
- for op in ("supports", "has_identity"):
87
- self.xmpp["xep_0030"].api.register(getattr(self.static, op), op)
88
-
89
- self._run_node_handler = disco._run_node_handler
90
-
91
- disco.cache_caps = self.cache_caps
92
- disco.update_caps = self.update_caps
93
- disco.assign_verstring = self.assign_verstring
94
- disco.get_verstring = self.get_verstring
95
-
96
- def plugin_end(self):
97
- self.xmpp["xep_0030"].del_feature(feature=stanza.Capabilities.namespace)
98
- self.xmpp.del_filter("out", self._filter_add_caps)
99
- self.xmpp.del_event_handler("entity_caps", self._process_caps)
100
- self.xmpp.remove_handler("Entity Capabilities")
101
- if not self.xmpp.is_component:
102
- self.xmpp.unregister_feature("caps", 10010)
103
- for op in ("supports", "has_identity"):
104
- self.xmpp["xep_0030"].restore_defaults(op)
105
-
106
- def session_bind(self, jid):
107
- self.xmpp["xep_0030"].add_feature(stanza.Capabilities.namespace)
108
-
109
- async def _filter_add_caps(self, stanza):
110
- if not isinstance(stanza, Presence) or not self.broadcast:
111
- return stanza
112
-
113
- if stanza["type"] not in ("available", "chat", "away", "dnd", "xa"):
114
- return stanza
115
-
116
- ver = await self.get_verstring(stanza["from"])
117
- if ver:
118
- stanza["caps"]["node"] = self.caps_node
119
- stanza["caps"]["hash"] = self.hash
120
- stanza["caps"]["ver"] = ver
121
- return stanza
122
-
123
- def _handle_caps(self, presence):
124
- if not self.xmpp.is_component:
125
- if presence["from"] == self.xmpp.boundjid:
126
- return
127
- self.xmpp.event("entity_caps", presence)
128
-
129
- def _handle_caps_feature(self, features):
130
- # We already have a method to process presence with
131
- # caps, so wrap things up and use that.
132
- p = Presence()
133
- p["from"] = self.xmpp.boundjid.domain
134
- p.append(features["caps"])
135
- self.xmpp.features.add("caps")
136
-
137
- self.xmpp.event("entity_caps", p)
138
-
139
- async def _process_caps(self, pres: Presence):
140
- if not pres["caps"]["hash"]:
141
- log.debug(
142
- "Received unsupported legacy caps: %s, %s, %s",
143
- pres["caps"]["node"],
144
- pres["caps"]["ver"],
145
- pres["caps"]["ext"],
146
- )
147
- self.xmpp.event("entity_caps_legacy", pres)
148
- return
149
-
150
- # if self.xmpp.is_component and pres.get_to() != self.xmpp.boundjid.bare:
151
- # log.debug("Not attempting to update caps for presence not directed at the component JID")
152
- # return
153
-
154
- async with self.lock:
155
- ver = pres["caps"]["ver"]
156
-
157
- existing_verstring = await self.get_verstring(pres["from"].full)
158
- if str(existing_verstring) == str(ver):
159
- return
160
-
161
- existing_caps = await self.get_caps(verstring=ver)
162
- if existing_caps is not None:
163
- await self.assign_verstring(pres["from"], ver)
164
- return
165
-
166
- ifrom = pres["to"] if self.xmpp.is_component else None
167
-
168
- if pres["caps"]["hash"] not in self.hashes:
169
- try:
170
- log.debug("Unknown caps hash: %s", pres["caps"]["hash"])
171
- self.xmpp["xep_0030"].get_info(jid=pres["from"], ifrom=ifrom)
172
- return
173
- except XMPPError:
174
- return
175
-
176
- log.debug("New caps verification string: %s", ver)
177
- try:
178
- node = "%s#%s" % (pres["caps"]["node"], ver)
179
- caps = await self.xmpp["xep_0030"].get_info(pres["from"], node, ifrom=ifrom)
180
-
181
- if isinstance(caps, Iq):
182
- caps = caps["disco_info"]
183
-
184
- if await self._validate_caps(
185
- caps, pres["caps"]["hash"], pres["caps"]["ver"]
186
- ):
187
- await self.assign_verstring(pres["from"], pres["caps"]["ver"])
188
- except XMPPError:
189
- log.debug("Could not retrieve disco#info results for caps for %s", node)
190
-
191
- async def _validate_caps(self, caps, hash, check_verstring):
192
- # Check Identities
193
- full_ids = caps.get_identities(dedupe=False)
194
- deduped_ids = caps.get_identities()
195
- if len(full_ids) != len(deduped_ids):
196
- log.debug("Duplicate disco identities found, invalid for caps")
197
- return False
198
-
199
- # Check Features
200
- full_features = caps.get_features(dedupe=False)
201
- deduped_features = caps.get_features()
202
- if len(full_features) != len(deduped_features):
203
- log.debug("Duplicate disco features found, invalid for caps")
204
- return False
205
-
206
- # Check Forms
207
- form_types = []
208
- deduped_form_types = set()
209
- for stanza in caps["substanzas"]:
210
- if not isinstance(stanza, self.xmpp["xep_0004"].stanza.Form):
211
- log.debug("Non form extension found, ignoring for caps")
212
- caps.xml.remove(stanza.xml)
213
- continue
214
- if "FORM_TYPE" in stanza.get_fields():
215
- f_type = tuple(stanza.get_fields()["FORM_TYPE"]["value"])
216
- form_types.append(f_type)
217
- deduped_form_types.add(f_type)
218
- if len(form_types) != len(deduped_form_types):
219
- log.debug("Duplicated FORM_TYPE values, " + "invalid for caps")
220
- return False
221
-
222
- if len(f_type) > 1:
223
- deduped_type = set(f_type)
224
- if len(f_type) != len(deduped_type):
225
- log.debug("Extra FORM_TYPE data, invalid for caps")
226
- return False
227
-
228
- if stanza.get_fields()["FORM_TYPE"]["type"] != "hidden":
229
- log.debug(
230
- "Field FORM_TYPE type not 'hidden', " + "ignoring form for caps"
231
- )
232
- caps.xml.remove(stanza.xml)
233
- else:
234
- log.debug("No FORM_TYPE found, ignoring form for caps")
235
- caps.xml.remove(stanza.xml)
236
-
237
- verstring = self.generate_verstring(caps, hash)
238
- if verstring != check_verstring:
239
- log.debug(
240
- "Verification strings do not match: %s, %s"
241
- % (verstring, check_verstring)
242
- )
243
- return False
244
-
245
- await self.cache_caps(verstring, caps)
246
- return True
247
-
248
- def generate_verstring(self, info, hash):
249
- hash = self.hashes.get(hash, None)
250
- if hash is None:
251
- return None
252
-
253
- S = ""
254
-
255
- # Convert None to '' in the identities
256
- def clean_identity(id):
257
- return map(lambda i: i or "", id)
258
-
259
- identities = map(clean_identity, info["identities"])
260
-
261
- identities = sorted(("/".join(i) for i in identities))
262
- features = sorted(info["features"])
263
-
264
- S += "<".join(identities) + "<"
265
- S += "<".join(features) + "<"
266
-
267
- form_types = {}
268
-
269
- for stanza in info["substanzas"]:
270
- if isinstance(stanza, self.xmpp["xep_0004"].stanza.Form):
271
- if "FORM_TYPE" in stanza.get_fields():
272
- f_type = stanza["values"]["FORM_TYPE"]
273
- if len(f_type):
274
- f_type = f_type[0]
275
- if f_type not in form_types:
276
- form_types[f_type] = []
277
- form_types[f_type].append(stanza)
278
-
279
- sorted_forms = sorted(form_types.keys())
280
- for f_type in sorted_forms:
281
- for form in form_types[f_type]:
282
- S += "%s<" % f_type
283
- fields = sorted(form.get_fields().keys())
284
- fields.remove("FORM_TYPE")
285
- for field in fields:
286
- S += "%s<" % field
287
- vals = form.get_fields()[field].get_value(convert=False)
288
- if vals is None:
289
- S += "<"
290
- else:
291
- if not isinstance(vals, list):
292
- vals = [vals]
293
- S += "<".join(sorted(vals)) + "<"
294
-
295
- binary = hash(S.encode("utf8")).digest()
296
- return base64.b64encode(binary).decode("utf-8")
297
-
298
- async def update_caps(
299
- self,
300
- jid: OptJidStr = None,
301
- node: Optional[str] = None,
302
- preserve: bool = False,
303
- broadcast: bool = True,
304
- ):
305
- """Update caps for a local JID based on current data.
306
-
307
- :param jid: JID whose info to update
308
- :param node: Node to fetch info from
309
- :param broadcast: Send a presence after updating.
310
- :param preserve: Send presence only to contacts found in the roster.
311
- """
312
- try:
313
- info = await self.xmpp["xep_0030"].get_info(jid, node, local=True)
314
- if isinstance(info, Iq):
315
- info = info["disco_info"]
316
- ver = self.generate_verstring(info, self.hash)
317
- await self.xmpp["xep_0030"].set_info(
318
- jid=jid, node="%s#%s" % (self.caps_node, ver), info=info
319
- )
320
- await self.cache_caps(ver, info)
321
- await self.assign_verstring(jid, ver)
322
-
323
- if broadcast and self.xmpp.sessionstarted and self.broadcast:
324
- if self.xmpp.is_component or preserve:
325
- for contact in self.xmpp.roster[jid]:
326
- self.xmpp.roster[jid][contact].send_last_presence()
327
- else:
328
- self.xmpp.roster[jid].send_last_presence()
329
- except XMPPError:
330
- return
331
-
332
- def get_verstring(self, jid=None) -> Future:
333
- """Get the stored verstring for a JID.
334
-
335
- .. versionchanged:: 1.8.0
336
- This function now returns a Future.
337
- """
338
- if jid in ("", None):
339
- jid = self.xmpp.boundjid.full
340
- if isinstance(jid, JID):
341
- jid = jid.full
342
- return self.api["get_verstring"](jid)
343
-
344
- def assign_verstring(self, jid=None, verstring=None) -> Future:
345
- """Assign a vertification string to a jid.
346
-
347
- .. versionchanged:: 1.8.0
348
- This function now returns a Future.
349
- """
350
- if jid in (None, ""):
351
- jid = self.xmpp.boundjid.full
352
- if isinstance(jid, JID):
353
- jid = jid.full
354
- return self.api["assign_verstring"](jid, args={"verstring": verstring})
355
-
356
- def cache_caps(self, verstring=None, info=None) -> Future:
357
- """Add caps to the cache.
358
-
359
- .. versionchanged:: 1.8.0
360
- This function now returns a Future.
361
- """
362
- data = {"verstring": verstring, "info": info}
363
- return self.api["cache_caps"](args=data)
364
-
365
- async def get_caps(self, jid=None, verstring=None):
366
- """Get caps for a JID.
367
-
368
- .. versionchanged:: 1.8.0
369
- This function is now a coroutine.
370
- """
371
- if verstring is None:
372
- if jid is not None:
373
- verstring = await self.get_verstring(jid)
374
- else:
375
- return None
376
- if isinstance(jid, JID):
377
- jid = jid.full
378
- data = {"verstring": verstring}
379
- return await self.api["get_caps"](jid, args=data)
@@ -1,16 +0,0 @@
1
-
2
- # Slixmpp: The Slick XMPP Library
3
- # Copyright (C) 2011 Nathanael C. Fritz, Lance J.T. Stout
4
- # This file is part of Slixmpp.
5
- # See the file LICENSE for copying permission.
6
- from __future__ import unicode_literals
7
-
8
- from slixmpp.xmlstream import ElementBase
9
-
10
-
11
- class Capabilities(ElementBase):
12
-
13
- namespace = 'http://jabber.org/protocol/caps'
14
- name = 'c'
15
- plugin_attrib = 'caps'
16
- interfaces = {'hash', 'node', 'ver', 'ext'}