Mercurial > libervia-backend
comparison libervia/backend/plugins/plugin_misc_file.py @ 4231:e11b13418ba6
plugin XEP-0353, XEP-0234, jingle: WebRTC data channel signaling implementation:
Implement XEP-0343: Signaling WebRTC Data Channels in Jingle. The current version of the
XEP (0.3.1) has no implementation and contains some flaws. After discussing this on xsf@,
Daniel (from Conversations) mentioned that they had a sprint with Larma (from Dino) to
work on another version and provided me with this link:
https://gist.github.com/iNPUTmice/6c56f3e948cca517c5fb129016d99e74 . I have used it for my
implementation.
This implementation reuses work done on Jingle A/V call (notably XEP-0176 and XEP-0167
plugins), with adaptations. When used, XEP-0234 will not handle the file itself as it
normally does. This is because WebRTC has several implementations (browser for web
interface, GStreamer for others), and file/data must be handled directly by the frontend.
This is particularly important for web frontends, as the file is not sent from the backend
but from the end-user's browser device.
Among the changes, there are:
- XEP-0343 implementation.
- `file_send` bridge method now use serialised dict as output.
- New `BaseTransportHandler.is_usable` method which get content data and returns a boolean
(default to `True`) to tell if this transport can actually be used in this context (when
we are initiator). Used in webRTC case to see if call data are available.
- Support of `application` media type, and everything necessary to handle data channels.
- Better confirmation message, with file name, size and description when available.
- When file is accepted in preflight, it is specified in following `action_new` signal for
actual file transfer. This way, frontend can avoid the display or 2 confirmation
messages.
- XEP-0166: when not specified, default `content` name is now its index number instead of
a UUID. This follows the behaviour of browsers.
- XEP-0353: better handling of events such as call taken by another device.
- various other updates.
rel 441
author | Goffi <goffi@goffi.org> |
---|---|
date | Sat, 06 Apr 2024 12:57:23 +0200 |
parents | 4b842c1fb686 |
children |
comparison
equal
deleted
inserted
replaced
4230:314d3c02bb67 | 4231:e11b13418ba6 |
---|---|
15 # GNU Affero General Public License for more details. | 15 # GNU Affero General Public License for more details. |
16 | 16 |
17 # You should have received a copy of the GNU Affero General Public License | 17 # You should have received a copy of the GNU Affero General Public License |
18 # along with this program. If not, see <http://www.gnu.org/licenses/>. | 18 # along with this program. If not, see <http://www.gnu.org/licenses/>. |
19 | 19 |
20 from functools import partial | |
20 import os | 21 import os |
21 import os.path | 22 import os.path |
22 from functools import partial | 23 from pathlib import Path |
24 | |
23 from twisted.internet import defer | 25 from twisted.internet import defer |
24 from twisted.words.protocols.jabber import jid | 26 from twisted.words.protocols.jabber import jid |
25 from libervia.backend.core.i18n import _, D_ | 27 |
28 from libervia.backend.core import exceptions | |
26 from libervia.backend.core.constants import Const as C | 29 from libervia.backend.core.constants import Const as C |
30 from libervia.backend.core.core_types import SatXMPPEntity | |
31 from libervia.backend.core.i18n import D_, _ | |
27 from libervia.backend.core.log import getLogger | 32 from libervia.backend.core.log import getLogger |
28 from libervia.backend.core import exceptions | |
29 from libervia.backend.tools import xml_tools | 33 from libervia.backend.tools import xml_tools |
30 from libervia.backend.tools import stream | 34 from libervia.backend.tools import stream |
31 from libervia.backend.tools import utils | 35 from libervia.backend.tools import utils |
32 from libervia.backend.tools.common import data_format, utils as common_utils | 36 from libervia.backend.tools.common import data_format, utils as common_utils |
33 | 37 |
71 self.host = host | 75 self.host = host |
72 host.bridge.add_method( | 76 host.bridge.add_method( |
73 "file_send", | 77 "file_send", |
74 ".plugin", | 78 ".plugin", |
75 in_sign="ssssss", | 79 in_sign="ssssss", |
76 out_sign="a{ss}", | 80 out_sign="s", |
77 method=self._file_send, | 81 method=self._file_send, |
78 async_=True, | 82 async_=True, |
79 ) | 83 ) |
80 self._file_managers = [] | 84 self._file_managers = [] |
81 host.import_menu( | 85 host.import_menu( |
94 file_desc: str, | 98 file_desc: str, |
95 extra_s: str, | 99 extra_s: str, |
96 profile: str = C.PROF_KEY_NONE | 100 profile: str = C.PROF_KEY_NONE |
97 ) -> defer.Deferred: | 101 ) -> defer.Deferred: |
98 client = self.host.get_client(profile) | 102 client = self.host.get_client(profile) |
99 return defer.ensureDeferred(self.file_send( | 103 d = defer.ensureDeferred(self.file_send( |
100 client, jid.JID(peer_jid_s), filepath, name or None, file_desc or None, | 104 client, jid.JID(peer_jid_s), filepath, name or None, file_desc or None, |
101 data_format.deserialise(extra_s) | 105 data_format.deserialise(extra_s) |
102 )) | 106 )) |
107 d.addCallback(data_format.serialise) | |
108 return d | |
103 | 109 |
104 async def file_send( | 110 async def file_send( |
105 self, client, peer_jid, filepath, filename=None, file_desc=None, extra=None | 111 self, |
106 ): | 112 client: SatXMPPEntity, |
113 peer_jid: jid.JID, | |
114 filepath: str|Path, | |
115 filename: str|None=None, | |
116 file_desc: str|None=None, | |
117 extra: dict|None=None | |
118 ) -> dict: | |
107 """Send a file using best available method | 119 """Send a file using best available method |
108 | 120 |
109 @param peer_jid(jid.JID): jid of the destinee | 121 @param peer_jid: jid of the destinee |
110 @param filepath(str): absolute path to the file | 122 @param filepath: absolute path to the file |
111 @param filename(unicode, None): name to use, or None to find it from filepath | 123 @param filename: name to use, or None to find it from filepath |
112 @param file_desc(unicode, None): description of the file | 124 @param file_desc: description of the file |
113 @param profile: %(doc_profile)s | 125 @param profile: %(doc_profile)s |
114 @return (dict): action dictionary, with progress id in case of success, else | 126 @return: action dictionary, with progress id in case of success, else |
115 xmlui message | 127 xmlui message |
116 """ | 128 """ |
117 if not os.path.isfile(filepath): | 129 filepath = Path(filepath) |
130 if not filepath.is_file(): | |
118 raise exceptions.DataError("The given path doesn't link to a file") | 131 raise exceptions.DataError("The given path doesn't link to a file") |
119 if not filename: | 132 if not filename: |
120 filename = os.path.basename(filepath) or "_" | 133 filename = filepath.name |
121 for manager, priority in self._file_managers: | 134 for manager, __ in self._file_managers: |
122 if await utils.as_deferred(manager.can_handle_file_send, | 135 if await utils.as_deferred(manager.can_handle_file_send, |
123 client, peer_jid, filepath): | 136 client, peer_jid, str(filepath)): |
124 try: | 137 try: |
125 method_name = manager.name | 138 method_name = manager.name |
126 except AttributeError: | 139 except AttributeError: |
127 method_name = manager.__class__.__name__ | 140 method_name = manager.__class__.__name__ |
128 log.info( | 141 log.info( |
129 _("{name} method will be used to send the file").format( | 142 _("{name} method will be used to send the file").format( |
130 name=method_name | 143 name=method_name |
131 ) | 144 ) |
132 ) | 145 ) |
133 try: | 146 try: |
134 progress_id = await utils.as_deferred( | 147 file_data= await utils.as_deferred( |
135 manager.file_send, client, peer_jid, filepath, filename, file_desc, | 148 manager.file_send, client, peer_jid, str(filepath), filename, file_desc, |
136 extra | 149 extra |
137 ) | 150 ) |
138 except Exception as e: | 151 except Exception as e: |
139 log.warning( | 152 log.warning( |
140 _("Can't send {filepath} to {peer_jid} with {method_name}: " | 153 _("Can't send {filepath} to {peer_jid} with {method_name}: " |
144 method_name=method_name, | 157 method_name=method_name, |
145 reason=e | 158 reason=e |
146 ) | 159 ) |
147 ) | 160 ) |
148 continue | 161 continue |
149 return {"progress": progress_id} | 162 if "progress" not in file_data: |
163 raise exceptions.InternalError( | |
164 '"progress" should be set in "file_send" returned file data dict.' | |
165 ) | |
166 return file_data | |
150 msg = "Can't find any method to send file to {jid}".format(jid=peer_jid.full()) | 167 msg = "Can't find any method to send file to {jid}".format(jid=peer_jid.full()) |
151 log.warning(msg) | 168 log.warning(msg) |
152 return { | 169 return { |
153 "xmlui": xml_tools.note( | 170 "xmlui": xml_tools.note( |
154 "Can't transfer file", msg, C.XMLUI_DATA_LVL_WARNING | 171 "Can't transfer file", msg, C.XMLUI_DATA_LVL_WARNING |