Mercurial > libervia-backend
comparison libervia/backend/plugins/plugin_xep_0422.py @ 4071:4b842c1fb686
refactoring: renamed `sat` package to `libervia.backend`
author | Goffi <goffi@goffi.org> |
---|---|
date | Fri, 02 Jun 2023 11:49:51 +0200 |
parents | sat/plugins/plugin_xep_0422.py@524856bd7b19 |
children | 0d7bb4df2343 |
comparison
equal
deleted
inserted
replaced
4070:d10748475025 | 4071:4b842c1fb686 |
---|---|
1 #!/usr/bin/env python3 | |
2 | |
3 # Copyright (C) 2009-2022 Jérôme Poisson (goffi@goffi.org) | |
4 | |
5 # This program is free software: you can redistribute it and/or modify | |
6 # it under the terms of the GNU Affero General Public License as published by | |
7 # the Free Software Foundation, either version 3 of the License, or | |
8 # (at your option) any later version. | |
9 | |
10 # This program is distributed in the hope that it will be useful, | |
11 # but WITHOUT ANY WARRANTY; without even the implied warranty of | |
12 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
13 # GNU Affero General Public License for more details. | |
14 | |
15 # You should have received a copy of the GNU Affero General Public License | |
16 # along with this program. If not, see <http://www.gnu.org/licenses/>. | |
17 | |
18 from typing import Optional, List, Tuple, Union, NamedTuple | |
19 from collections import namedtuple | |
20 | |
21 from twisted.words.protocols.jabber import xmlstream | |
22 from twisted.words.xish import domish | |
23 from wokkel import disco | |
24 from zope.interface import implementer | |
25 | |
26 from libervia.backend.core.constants import Const as C | |
27 from libervia.backend.core.i18n import _ | |
28 from libervia.backend.core.log import getLogger | |
29 from libervia.backend.core.core_types import SatXMPPEntity | |
30 from libervia.backend.memory.sqla_mapping import History | |
31 from libervia.backend.tools.common.async_utils import async_lru | |
32 | |
33 log = getLogger(__name__) | |
34 | |
35 | |
36 PLUGIN_INFO = { | |
37 C.PI_NAME: "Message Fastening", | |
38 C.PI_IMPORT_NAME: "XEP-0422", | |
39 C.PI_TYPE: "XEP", | |
40 C.PI_MODES: C.PLUG_MODE_BOTH, | |
41 C.PI_PROTOCOLS: ["XEP-0359", "XEP-0422"], | |
42 C.PI_MAIN: "XEP_0422", | |
43 C.PI_HANDLER: "yes", | |
44 C.PI_DESCRIPTION: _("""Implementation Message Fastening"""), | |
45 } | |
46 | |
47 NS_FASTEN = "urn:xmpp:fasten:0" | |
48 | |
49 | |
50 class FastenMetadata(NamedTuple): | |
51 elements: List[domish.Element] | |
52 id: str | |
53 history: Optional[History] | |
54 clear: bool | |
55 shell: bool | |
56 | |
57 | |
58 class XEP_0422(object): | |
59 | |
60 def __init__(self, host): | |
61 log.info(_("XEP-0422 (Message Fastening) plugin initialization")) | |
62 self.host = host | |
63 host.register_namespace("fasten", NS_FASTEN) | |
64 | |
65 def get_handler(self, __): | |
66 return XEP_0422_handler() | |
67 | |
68 def apply_to_elt( | |
69 self, | |
70 message_elt: domish.Element, | |
71 origin_id: str, | |
72 clear: Optional[bool] = None, | |
73 shell: Optional[bool] = None, | |
74 children: Optional[List[domish.Element]] = None, | |
75 external: Optional[List[Union[str, Tuple[str, str]]]] = None | |
76 ) -> domish.Element: | |
77 """Generate, add and return <apply-to> element | |
78 | |
79 @param message_elt: wrapping <message> element | |
80 @param origin_id: origin ID of the target message | |
81 @param clear: set to True to remove a fastening | |
82 @param shell: set to True when using e2ee shell | |
83 cf. https://xmpp.org/extensions/xep-0422.html#encryption | |
84 @param children: element to fasten to the target message | |
85 <apply-to> element is returned, thus children can also easily be added | |
86 afterwards | |
87 @param external: <external> element to add | |
88 cf. https://xmpp.org/extensions/xep-0422.html#external-payloads | |
89 the list items can either be a str with only the element name, | |
90 or a tuple which must then be (namespace, name) | |
91 @return: <apply-to> element, which is already added to the wrapping message_elt | |
92 """ | |
93 apply_to_elt = message_elt.addElement((NS_FASTEN, "apply-to")) | |
94 apply_to_elt["id"] = origin_id | |
95 if clear is not None: | |
96 apply_to_elt["clear"] = C.bool_const(clear) | |
97 if shell is not None: | |
98 apply_to_elt["shell"] = C.bool_const(shell) | |
99 if children is not None: | |
100 for child in children: | |
101 apply_to_elt.addChild(child) | |
102 if external is not None: | |
103 for ext in external: | |
104 external_elt = apply_to_elt.addElement("external") | |
105 if isinstance(ext, str): | |
106 external_elt["name"] = ext | |
107 else: | |
108 ns, name = ext | |
109 external_elt["name"] = name | |
110 external_elt["element-namespace"] = ns | |
111 return apply_to_elt | |
112 | |
113 @async_lru(maxsize=5) | |
114 async def get_fastened_elts( | |
115 self, | |
116 client: SatXMPPEntity, | |
117 message_elt: domish.Element | |
118 ) -> Optional[FastenMetadata]: | |
119 """Get fastened elements | |
120 | |
121 if the message contains no <apply-to> element, None is returned | |
122 """ | |
123 try: | |
124 apply_to_elt = next(message_elt.elements(NS_FASTEN, "apply-to")) | |
125 except StopIteration: | |
126 return None | |
127 else: | |
128 origin_id = apply_to_elt.getAttribute("id") | |
129 if not origin_id: | |
130 log.warning( | |
131 f"Received invalid fastening message: {message_elt.toXml()}" | |
132 ) | |
133 return None | |
134 elements = apply_to_elt.children | |
135 if not elements: | |
136 log.warning(f"No element to fasten: {message_elt.toXml()}") | |
137 return None | |
138 history = await self.host.memory.storage.get( | |
139 client, | |
140 History, | |
141 History.origin_id, | |
142 origin_id, | |
143 (History.messages, History.subjects, History.thread) | |
144 ) | |
145 return FastenMetadata( | |
146 elements, | |
147 origin_id, | |
148 history, | |
149 C.bool(apply_to_elt.getAttribute("clear", C.BOOL_FALSE)), | |
150 C.bool(apply_to_elt.getAttribute("shell", C.BOOL_FALSE)), | |
151 ) | |
152 | |
153 | |
154 @implementer(disco.IDisco) | |
155 class XEP_0422_handler(xmlstream.XMPPHandler): | |
156 | |
157 def getDiscoInfo(self, __, target, nodeIdentifier=""): | |
158 return [disco.DiscoFeature(NS_FASTEN)] | |
159 | |
160 def getDiscoItems(self, requestor, target, nodeIdentifier=""): | |
161 return [] |