Mercurial > libervia-backend
comparison sat/plugins/plugin_xep_0191.py @ 3787:f8a0f3b65371
plugin XEP-0191: Blocking Command implementation:
rel 367
author | Goffi <goffi@goffi.org> |
---|---|
date | Fri, 27 May 2022 12:12:16 +0200 |
parents | |
children | 24fbc4cad534 |
comparison
equal
deleted
inserted
replaced
3786:cebfdfff3e99 | 3787:f8a0f3b65371 |
---|---|
1 #!/usr/bin/env python3 | |
2 | |
3 # Libervia plugin for XEP-0191 | |
4 # Copyright (C) 2009-2022 Jérôme Poisson (goffi@goffi.org) | |
5 | |
6 # This program is free software: you can redistribute it and/or modify | |
7 # it under the terms of the GNU Affero General Public License as published by | |
8 # the Free Software Foundation, either version 3 of the License, or | |
9 # (at your option) any later version. | |
10 | |
11 # This program is distributed in the hope that it will be useful, | |
12 # but WITHOUT ANY WARRANTY; without even the implied warranty of | |
13 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
14 # GNU Affero General Public License for more details. | |
15 | |
16 # You should have received a copy of the GNU Affero General Public License | |
17 # along with this program. If not, see <http://www.gnu.org/licenses/>. | |
18 | |
19 from typing import List, Set | |
20 | |
21 from twisted.words.protocols.jabber import xmlstream, jid | |
22 from twisted.words.xish import domish | |
23 from twisted.internet import defer | |
24 from zope.interface import implementer | |
25 from wokkel import disco, iwokkel | |
26 | |
27 from sat.core.constants import Const as C | |
28 from sat.core.i18n import _ | |
29 from sat.core.log import getLogger | |
30 from sat.core.core_types import SatXMPPEntity | |
31 from sat.tools.utils import ensure_deferred | |
32 | |
33 log = getLogger(__name__) | |
34 | |
35 PLUGIN_INFO = { | |
36 C.PI_NAME: "Pubsub Public Subscriptions", | |
37 C.PI_IMPORT_NAME: "XEP-0191", | |
38 C.PI_TYPE: C.PLUG_TYPE_XEP, | |
39 C.PI_MODES: C.PLUG_MODE_BOTH, | |
40 C.PI_PROTOCOLS: ["XEP-0191"], | |
41 C.PI_DEPENDENCIES: ["XEP-0060", "XEP-0376"], | |
42 C.PI_MAIN: "XEP_0191", | |
43 C.PI_HANDLER: "yes", | |
44 C.PI_DESCRIPTION: _("""Pubsub Public Subscriptions implementation"""), | |
45 } | |
46 | |
47 NS_BLOCKING = "urn:xmpp:blocking" | |
48 IQ_BLOCK_PUSH = f'{C.IQ_SET}/block[@xmlns="{NS_BLOCKING}"]' | |
49 IQ_UNBLOCK_PUSH = f'{C.IQ_SET}/unblock[@xmlns="{NS_BLOCKING}"]' | |
50 | |
51 | |
52 class XEP_0191: | |
53 | |
54 def __init__(self, host): | |
55 log.info(_("Blocking Command initialization")) | |
56 host.registerNamespace("blocking", NS_BLOCKING) | |
57 self.host = host | |
58 host.bridge.addMethod( | |
59 "blockingList", | |
60 ".plugin", | |
61 in_sign="s", | |
62 out_sign="as", | |
63 method=self._blockList, | |
64 async_=True, | |
65 ) | |
66 host.bridge.addMethod( | |
67 "blockingBlock", | |
68 ".plugin", | |
69 in_sign="ass", | |
70 out_sign="", | |
71 method=self._block, | |
72 async_=True, | |
73 ) | |
74 host.bridge.addMethod( | |
75 "blockingUnblock", | |
76 ".plugin", | |
77 in_sign="ass", | |
78 out_sign="", | |
79 method=self._unblock, | |
80 async_=True, | |
81 ) | |
82 | |
83 def getHandler(self, client): | |
84 return XEP_0191_Handler(self) | |
85 | |
86 @ensure_deferred | |
87 async def _blockList( | |
88 self, | |
89 profile_key=C.PROF_KEY_NONE | |
90 ) -> List[str]: | |
91 client = self.host.getClient(profile_key) | |
92 blocked_jids = await self.blockList(client) | |
93 return [j.full() for j in blocked_jids] | |
94 | |
95 async def blockList(self, client: SatXMPPEntity) -> Set[jid.JID]: | |
96 await self.host.checkFeature(client, NS_BLOCKING) | |
97 iq_elt = client.IQ("get") | |
98 iq_elt.addElement((NS_BLOCKING, "blocklist")) | |
99 iq_result_elt = await iq_elt.send() | |
100 try: | |
101 blocklist_elt = next(iq_result_elt.elements(NS_BLOCKING, "blocklist")) | |
102 except StopIteration: | |
103 log.warning(f"missing <blocklist> element: {iq_result_elt.toXml()}") | |
104 return [] | |
105 blocked_jids = set() | |
106 for item_elt in blocklist_elt.elements(NS_BLOCKING, "item"): | |
107 try: | |
108 blocked_jid = jid.JID(item_elt["jid"]) | |
109 except (RuntimeError, AttributeError): | |
110 log.warning(f"Invalid <item> element in block list: {item_elt.toXml()}") | |
111 else: | |
112 blocked_jids.add(blocked_jid) | |
113 | |
114 return blocked_jids | |
115 | |
116 def _block( | |
117 self, | |
118 entities: List[str], | |
119 profile_key: str = C.PROF_KEY_NONE | |
120 ) -> str: | |
121 client = self.host.getClient(profile_key) | |
122 return defer.ensureDeferred( | |
123 self.block(client, [jid.JID(entity) for entity in entities]) | |
124 ) | |
125 | |
126 async def block(self, client: SatXMPPEntity, entities: List[jid.JID]) -> None: | |
127 await self.host.checkFeature(client, NS_BLOCKING) | |
128 iq_elt = client.IQ("set") | |
129 block_elt = iq_elt.addElement((NS_BLOCKING, "block")) | |
130 for entity in entities: | |
131 item_elt = block_elt.addElement("item") | |
132 item_elt["jid"] = entity.full() | |
133 await iq_elt.send() | |
134 | |
135 def _unblock( | |
136 self, | |
137 entities: List[str], | |
138 profile_key: str = C.PROF_KEY_NONE | |
139 ) -> None: | |
140 client = self.host.getClient(profile_key) | |
141 return defer.ensureDeferred( | |
142 self.unblock(client, [jid.JID(e) for e in entities]) | |
143 ) | |
144 | |
145 async def unblock(self, client: SatXMPPEntity, entities: List[jid.JID]) -> None: | |
146 await self.host.checkFeature(client, NS_BLOCKING) | |
147 iq_elt = client.IQ("set") | |
148 unblock_elt = iq_elt.addElement((NS_BLOCKING, "unblock")) | |
149 for entity in entities: | |
150 item_elt = unblock_elt.addElement("item") | |
151 item_elt["jid"] = entity.full() | |
152 await iq_elt.send() | |
153 | |
154 def onBlockPush(self, iq_elt: domish.Element, client: SatXMPPEntity) -> None: | |
155 # TODO: send notification to user | |
156 iq_elt.handled = True | |
157 for item_elt in iq_elt.block.elements(NS_BLOCKING, "item"): | |
158 try: | |
159 entity = jid.JID(item_elt["jid"]) | |
160 except (KeyError, RuntimeError): | |
161 log.warning(f"invalid item received in block push: {item_elt.toXml()}") | |
162 else: | |
163 log.info(f"{entity.full()} has been blocked for {client.profile}") | |
164 iq_result_elt = xmlstream.toResponse(iq_elt, "result") | |
165 client.send(iq_result_elt) | |
166 | |
167 def onUnblockPush(self, iq_elt: domish.Element, client: SatXMPPEntity) -> None: | |
168 # TODO: send notification to user | |
169 iq_elt.handled = True | |
170 items = list(iq_elt.unblock.elements(NS_BLOCKING, "item")) | |
171 if not items: | |
172 log.info(f"All entities have been unblocked for {client.profile}") | |
173 else: | |
174 for item_elt in items: | |
175 try: | |
176 entity = jid.JID(item_elt["jid"]) | |
177 except (KeyError, RuntimeError): | |
178 log.warning( | |
179 f"invalid item received in unblock push: {item_elt.toXml()}" | |
180 ) | |
181 else: | |
182 log.info(f"{entity.full()} has been unblocked for {client.profile}") | |
183 iq_result_elt = xmlstream.toResponse(iq_elt, "result") | |
184 client.send(iq_result_elt) | |
185 | |
186 | |
187 @implementer(iwokkel.IDisco) | |
188 class XEP_0191_Handler(xmlstream.XMPPHandler): | |
189 | |
190 def __init__(self, plugin_parent: XEP_0191): | |
191 self.plugin_parent = plugin_parent | |
192 | |
193 def connectionInitialized(self): | |
194 self.xmlstream.addObserver( | |
195 IQ_BLOCK_PUSH, | |
196 self.plugin_parent.onBlockPush, | |
197 client=self.parent | |
198 | |
199 ) | |
200 self.xmlstream.addObserver( | |
201 IQ_UNBLOCK_PUSH, | |
202 self.plugin_parent.onUnblockPush, | |
203 client=self.parent | |
204 ) | |
205 | |
206 def getDiscoInfo(self, requestor, service, nodeIdentifier=""): | |
207 return [disco.DiscoFeature(NS_BLOCKING)] | |
208 | |
209 def getDiscoItems(self, requestor, service, nodeIdentifier=""): | |
210 return [] |