Mercurial > libervia-backend
comparison src/plugins/plugin_misc_file.py @ 2502:7ad5f2c4e34a
XEP-0065,XEP-0096,XEP-0166,XEP-0235,XEP-0300: file transfer improvments:
huge patch sorry :)
many things are improved by this patch, notably:
- updated to last protocol changes (urn:xmpp:jingle:apps:file-transfer:5 and urn:xmpp:hashes:2)
- XEP-0234: file request implementation
- XEP-0234: methods to parse and generate <file> element (can be used by other plugins easily)
- XEP-0234: range data is now in a namedtuple
- path and namespace can be specified when sending/requesting a file (not standard, but needed for file sharing)
- better error/termination handling
- trigger points to handle file requests by other plugins
- preparation to use file plugins with components
author | Goffi <goffi@goffi.org> |
---|---|
date | Wed, 28 Feb 2018 18:28:39 +0100 |
parents | e2a7bb875957 |
children |
comparison
equal
deleted
inserted
replaced
2501:3b67fe672206 | 2502:7ad5f2c4e34a |
---|---|
32 | 32 |
33 PLUGIN_INFO = { | 33 PLUGIN_INFO = { |
34 C.PI_NAME: "File Tansfer", | 34 C.PI_NAME: "File Tansfer", |
35 C.PI_IMPORT_NAME: "FILE", | 35 C.PI_IMPORT_NAME: "FILE", |
36 C.PI_TYPE: C.PLUG_TYPE_MISC, | 36 C.PI_TYPE: C.PLUG_TYPE_MISC, |
37 C.PI_MODES: C.PLUG_MODE_BOTH, | |
37 C.PI_MAIN: "FilePlugin", | 38 C.PI_MAIN: "FilePlugin", |
38 C.PI_HANDLER: "no", | 39 C.PI_HANDLER: "no", |
39 C.PI_DESCRIPTION: _("""File Tansfer Management: | 40 C.PI_DESCRIPTION: _("""File Tansfer Management: |
40 This plugin manage the various ways of sending a file, and choose the best one.""") | 41 This plugin manage the various ways of sending a file, and choose the best one.""") |
41 } | 42 } |
56 File=stream.SatFile | 57 File=stream.SatFile |
57 | 58 |
58 def __init__(self, host): | 59 def __init__(self, host): |
59 log.info(_("plugin File initialization")) | 60 log.info(_("plugin File initialization")) |
60 self.host = host | 61 self.host = host |
61 host.bridge.addMethod("fileSend", ".plugin", in_sign='sssss', out_sign='a{ss}', method=self._fileSend, async=True) | 62 host.bridge.addMethod("fileSend", ".plugin", in_sign='ssssa{ss}s', out_sign='a{ss}', method=self._fileSend, async=True) |
62 self._file_callbacks = [] | 63 self._file_callbacks = [] |
63 host.importMenu((D_("Action"), D_("send file")), self._fileSendMenu, security_limit=10, help_string=D_("Send a file"), type_=C.MENU_SINGLE) | 64 host.importMenu((D_("Action"), D_("send file")), self._fileSendMenu, security_limit=10, help_string=D_("Send a file"), type_=C.MENU_SINGLE) |
64 | 65 |
65 def _fileSend(self, peer_jid_s, filepath, name="", file_desc="", profile=C.PROF_KEY_NONE): | 66 def _fileSend(self, peer_jid_s, filepath, name="", file_desc="", extra=None, profile=C.PROF_KEY_NONE): |
66 client = self.host.getClient(profile) | 67 client = self.host.getClient(profile) |
67 return self.fileSend(client, jid.JID(peer_jid_s), filepath, name or None, file_desc or None) | 68 return self.fileSend(client, jid.JID(peer_jid_s), filepath, name or None, file_desc or None, extra) |
68 | 69 |
69 @defer.inlineCallbacks | 70 @defer.inlineCallbacks |
70 def fileSend(self, client, peer_jid, filepath, filename=None, file_desc=None): | 71 def fileSend(self, client, peer_jid, filepath, filename=None, file_desc=None, extra=None): |
71 """Send a file using best available method | 72 """Send a file using best available method |
72 | 73 |
73 @param peer_jid(jid.JID): jid of the destinee | 74 @param peer_jid(jid.JID): jid of the destinee |
74 @param filepath(str): absolute path to the file | 75 @param filepath(str): absolute path to the file |
75 @param filename(unicode, None): name to use, or None to find it from filepath | 76 @param filename(unicode, None): name to use, or None to find it from filepath |
83 filename = os.path.basename(filepath) or '_' | 84 filename = os.path.basename(filepath) or '_' |
84 for namespace, callback, priority, method_name in self._file_callbacks: | 85 for namespace, callback, priority, method_name in self._file_callbacks: |
85 has_feature = yield self.host.hasFeature(client, namespace, peer_jid) | 86 has_feature = yield self.host.hasFeature(client, namespace, peer_jid) |
86 if has_feature: | 87 if has_feature: |
87 log.info(u"{name} method will be used to send the file".format(name=method_name)) | 88 log.info(u"{name} method will be used to send the file".format(name=method_name)) |
88 progress_id = yield callback(client, peer_jid, filepath, filename, file_desc) | 89 progress_id = yield callback(client, peer_jid, filepath, filename, file_desc, extra) |
89 defer.returnValue({'progress': progress_id}) | 90 defer.returnValue({'progress': progress_id}) |
90 msg = u"Can't find any method to send file to {jid}".format(jid=peer_jid.full()) | 91 msg = u"Can't find any method to send file to {jid}".format(jid=peer_jid.full()) |
91 log.warning(msg) | 92 log.warning(msg) |
92 defer.returnValue({'xmlui': xml_tools.note(u"Can't transfer file", msg, C.XMLUI_DATA_LVL_WARNING).toXml()}) | 93 defer.returnValue({'xmlui': xml_tools.note(u"Can't transfer file", msg, C.XMLUI_DATA_LVL_WARNING).toXml()}) |
93 | 94 |
141 raise exceptions.NotFound(u"The namespace to unregister doesn't exist") | 142 raise exceptions.NotFound(u"The namespace to unregister doesn't exist") |
142 | 143 |
143 # Dialogs with user | 144 # Dialogs with user |
144 # the overwrite check is done here | 145 # the overwrite check is done here |
145 | 146 |
146 def _openFileWrite(self, client, file_path, transfer_data, file_data, stream_object): | 147 def openFileWrite(self, client, file_path, transfer_data, file_data, stream_object): |
148 """create SatFile or FileStremaObject for the requested file and fill suitable data | |
149 """ | |
147 if stream_object: | 150 if stream_object: |
148 assert 'stream_object' not in transfer_data | 151 assert 'stream_object' not in transfer_data |
149 transfer_data['stream_object'] = stream.FileStreamObject( | 152 transfer_data['stream_object'] = stream.FileStreamObject( |
150 self.host, | 153 self.host, |
151 client, | 154 client, |
186 | 189 |
187 # we manage case where file already exists | 190 # we manage case where file already exists |
188 if os.path.exists(file_path): | 191 if os.path.exists(file_path): |
189 def check_overwrite(overwrite): | 192 def check_overwrite(overwrite): |
190 if overwrite: | 193 if overwrite: |
191 self._openFileWrite(client, file_path, transfer_data, file_data, stream_object) | 194 self.openFileWrite(client, file_path, transfer_data, file_data, stream_object) |
192 return True | 195 return True |
193 else: | 196 else: |
194 return self.getDestDir(client, peer_jid, transfer_data, file_data) | 197 return self.getDestDir(client, peer_jid, transfer_data, file_data) |
195 | 198 |
196 exists_d = xml_tools.deferConfirm( | 199 exists_d = xml_tools.deferConfirm( |
204 security_limit=SECURITY_LIMIT, | 207 security_limit=SECURITY_LIMIT, |
205 profile=client.profile) | 208 profile=client.profile) |
206 exists_d.addCallback(check_overwrite) | 209 exists_d.addCallback(check_overwrite) |
207 return exists_d | 210 return exists_d |
208 | 211 |
209 self._openFileWrite(client, file_path, transfer_data, file_data, stream_object) | 212 self.openFileWrite(client, file_path, transfer_data, file_data, stream_object) |
210 return True | 213 return True |
211 | 214 |
212 def getDestDir(self, client, peer_jid, transfer_data, file_data, stream_object=False): | 215 def getDestDir(self, client, peer_jid, transfer_data, file_data, stream_object=False): |
213 """Request confirmation and destination dir to user | 216 """Request confirmation and destination dir to user |
214 | 217 |
234 "size_human" will also be added with human readable file size | 237 "size_human" will also be added with human readable file size |
235 @param stream_object(bool): if True, a stream_object will be used instead of file_obj | 238 @param stream_object(bool): if True, a stream_object will be used instead of file_obj |
236 a stream.FileStreamObject will be used | 239 a stream.FileStreamObject will be used |
237 return (defer.Deferred): True if transfer is accepted | 240 return (defer.Deferred): True if transfer is accepted |
238 """ | 241 """ |
242 cont,ret_value = self.host.trigger.returnPoint("FILE_getDestDir", client, peer_jid, transfer_data, file_data, stream_object) | |
243 if not cont: | |
244 return ret_value | |
239 filename = file_data['name'] | 245 filename = file_data['name'] |
240 assert filename and not '/' in filename | 246 assert filename and not '/' in filename |
241 assert PROGRESS_ID_KEY in file_data | 247 assert PROGRESS_ID_KEY in file_data |
242 # human readable size | 248 # human readable size |
243 file_data['size_human'] = u'{:.6n} Mio'.format(float(file_data['size'])/(1024**2)) | 249 file_data['size_human'] = u'{:.6n} Mio'.format(float(file_data['size'])/(1024**2)) |