Mercurial > libervia-backend
comparison sat/plugins/plugin_xep_0329.py @ 2624:56f94936df1e
code style reformatting using black
author | Goffi <goffi@goffi.org> |
---|---|
date | Wed, 27 Jun 2018 20:14:46 +0200 |
parents | 282d1314d574 |
children | 003b8b4b56a7 |
comparison
equal
deleted
inserted
replaced
2623:49533de4540b | 2624:56f94936df1e |
---|---|
19 | 19 |
20 from sat.core.i18n import _ | 20 from sat.core.i18n import _ |
21 from sat.core import exceptions | 21 from sat.core import exceptions |
22 from sat.core.constants import Const as C | 22 from sat.core.constants import Const as C |
23 from sat.core.log import getLogger | 23 from sat.core.log import getLogger |
24 | |
24 log = getLogger(__name__) | 25 log = getLogger(__name__) |
25 from sat.tools import stream | 26 from sat.tools import stream |
26 from sat.tools.common import regex | 27 from sat.tools.common import regex |
27 from wokkel import disco, iwokkel | 28 from wokkel import disco, iwokkel |
28 from zope.interface import implements | 29 from zope.interface import implements |
42 C.PI_MODES: C.PLUG_MODE_BOTH, | 43 C.PI_MODES: C.PLUG_MODE_BOTH, |
43 C.PI_PROTOCOLS: ["XEP-0329"], | 44 C.PI_PROTOCOLS: ["XEP-0329"], |
44 C.PI_DEPENDENCIES: ["XEP-0234", "XEP-0300"], | 45 C.PI_DEPENDENCIES: ["XEP-0234", "XEP-0300"], |
45 C.PI_MAIN: "XEP_0329", | 46 C.PI_MAIN: "XEP_0329", |
46 C.PI_HANDLER: "yes", | 47 C.PI_HANDLER: "yes", |
47 C.PI_DESCRIPTION: _(u"""Implementation of File Information Sharing""") | 48 C.PI_DESCRIPTION: _(u"""Implementation of File Information Sharing"""), |
48 } | 49 } |
49 | 50 |
50 NS_FIS = 'urn:xmpp:fis:0' | 51 NS_FIS = "urn:xmpp:fis:0" |
51 | 52 |
52 IQ_FIS_REQUEST = C.IQ_GET + '/query[@xmlns="' + NS_FIS + '"]' | 53 IQ_FIS_REQUEST = C.IQ_GET + '/query[@xmlns="' + NS_FIS + '"]' |
53 SINGLE_FILES_DIR = u"files" | 54 SINGLE_FILES_DIR = u"files" |
54 TYPE_VIRTUAL= u'virtual' | 55 TYPE_VIRTUAL = u"virtual" |
55 TYPE_PATH = u'path' | 56 TYPE_PATH = u"path" |
56 SHARE_TYPES = (TYPE_PATH, TYPE_VIRTUAL) | 57 SHARE_TYPES = (TYPE_PATH, TYPE_VIRTUAL) |
57 KEY_TYPE = u'type' | 58 KEY_TYPE = u"type" |
58 | 59 |
59 | 60 |
60 class ShareNode(object): | 61 class ShareNode(object): |
61 """node containing directory or files to share, virtual or real""" | 62 """node containing directory or files to share, virtual or real""" |
63 | |
62 host = None | 64 host = None |
63 | 65 |
64 def __init__(self, name, parent, type_, access, path=None): | 66 def __init__(self, name, parent, type_, access, path=None): |
65 assert type_ in SHARE_TYPES | 67 assert type_ in SHARE_TYPES |
66 if name is not None: | 68 if name is not None: |
67 if name == u'..' or u'/' in name or u'\\' in name: | 69 if name == u".." or u"/" in name or u"\\" in name: |
68 log.warning(_(u'path change chars found in name [{name}], hack attempt?').format(name=name)) | 70 log.warning( |
69 if name == u'..': | 71 _(u"path change chars found in name [{name}], hack attempt?").format( |
70 name = u'--' | 72 name=name |
73 ) | |
74 ) | |
75 if name == u"..": | |
76 name = u"--" | |
71 else: | 77 else: |
72 name = regex.pathEscape(name) | 78 name = regex.pathEscape(name) |
73 self.name = name | 79 self.name = name |
74 self.children = {} | 80 self.children = {} |
75 self.type = type_ | 81 self.type = type_ |
123 | 129 |
124 def removeFromParent(self): | 130 def removeFromParent(self): |
125 try: | 131 try: |
126 del self.parent.children[self.name] | 132 del self.parent.children[self.name] |
127 except TypeError: | 133 except TypeError: |
128 raise exceptions.InternalError(u'trying to remove a node from inexisting parent') | 134 raise exceptions.InternalError( |
135 u"trying to remove a node from inexisting parent" | |
136 ) | |
129 except KeyError: | 137 except KeyError: |
130 raise exceptions.InternalError(u"node not found in parent's children") | 138 raise exceptions.InternalError(u"node not found in parent's children") |
131 self.parent = None | 139 self.parent = None |
132 | 140 |
133 def _checkNodePermission(self, client, node, perms, peer_jid): | 141 def _checkNodePermission(self, client, node, perms, peer_jid): |
136 @param node(SharedNode): node to check access | 144 @param node(SharedNode): node to check access |
137 @param perms(unicode): permissions to check, iterable of C.ACCESS_PERM_* | 145 @param perms(unicode): permissions to check, iterable of C.ACCESS_PERM_* |
138 @param peer_jid(jid.JID): entity which try to access the node | 146 @param peer_jid(jid.JID): entity which try to access the node |
139 @return (bool): True if entity can access | 147 @return (bool): True if entity can access |
140 """ | 148 """ |
141 file_data = {u'access':self.access, u'owner': client.jid.userhostJID()} | 149 file_data = {u"access": self.access, u"owner": client.jid.userhostJID()} |
142 try: | 150 try: |
143 self.host.memory.checkFilePermission(file_data, peer_jid, perms) | 151 self.host.memory.checkFilePermission(file_data, peer_jid, perms) |
144 except exceptions.PermissionError: | 152 except exceptions.PermissionError: |
145 return False | 153 return False |
146 else: | 154 else: |
147 return True | 155 return True |
148 | 156 |
149 def checkPermissions(self, client, peer_jid, perms=(C.ACCESS_PERM_READ,), check_parents=True): | 157 def checkPermissions( |
158 self, client, peer_jid, perms=(C.ACCESS_PERM_READ,), check_parents=True | |
159 ): | |
150 """check that peer_jid can access this node and all its parents | 160 """check that peer_jid can access this node and all its parents |
151 | 161 |
152 @param peer_jid(jid.JID): entrity trying to access the node | 162 @param peer_jid(jid.JID): entrity trying to access the node |
153 @param perms(unicode): permissions to check, iterable of C.ACCESS_PERM_* | 163 @param perms(unicode): permissions to check, iterable of C.ACCESS_PERM_* |
154 @param check_parents(bool): if True, access of all parents of this node will be checked too | 164 @param check_parents(bool): if True, access of all parents of this node will be checked too |
176 @return (dict, unicode): shared data, remaining path | 186 @return (dict, unicode): shared data, remaining path |
177 @raise exceptions.PermissionError: user can't access this file | 187 @raise exceptions.PermissionError: user can't access this file |
178 @raise exceptions.DataError: path is invalid | 188 @raise exceptions.DataError: path is invalid |
179 @raise NotFound: path lead to a non existing file/directory | 189 @raise NotFound: path lead to a non existing file/directory |
180 """ | 190 """ |
181 path_elts = filter(None, path.split(u'/')) | 191 path_elts = filter(None, path.split(u"/")) |
182 | 192 |
183 if u'..' in path_elts: | 193 if u".." in path_elts: |
184 log.warning(_(u'parent dir ("..") found in path, hack attempt? path is {path} [{profile}]').format( | 194 log.warning( |
185 path=path, profile=client.profile)) | 195 _( |
196 u'parent dir ("..") found in path, hack attempt? path is {path} [{profile}]' | |
197 ).format(path=path, profile=client.profile) | |
198 ) | |
186 raise exceptions.PermissionError(u"illegal path elements") | 199 raise exceptions.PermissionError(u"illegal path elements") |
187 | 200 |
188 if not path_elts: | 201 if not path_elts: |
189 raise exceptions.DataError(_(u'path is invalid: {path}').format(path=path)) | 202 raise exceptions.DataError(_(u"path is invalid: {path}").format(path=path)) |
190 | 203 |
191 node = client._XEP_0329_root_node | 204 node = client._XEP_0329_root_node |
192 | 205 |
193 while path_elts: | 206 while path_elts: |
194 if node.type == TYPE_VIRTUAL: | 207 if node.type == TYPE_VIRTUAL: |
197 except KeyError: | 210 except KeyError: |
198 raise exceptions.NotFound | 211 raise exceptions.NotFound |
199 elif node.type == TYPE_PATH: | 212 elif node.type == TYPE_PATH: |
200 break | 213 break |
201 | 214 |
202 if not node.checkPermissions(client, peer_jid, perms = perms): | 215 if not node.checkPermissions(client, peer_jid, perms=perms): |
203 raise exceptions.PermissionError(u"permission denied") | 216 raise exceptions.PermissionError(u"permission denied") |
204 | 217 |
205 return node, u'/'.join(path_elts) | 218 return node, u"/".join(path_elts) |
206 | 219 |
207 def findByLocalPath(self, path): | 220 def findByLocalPath(self, path): |
208 """retrieve nodes linking to local path | 221 """retrieve nodes linking to local path |
209 | 222 |
210 @return (list[ShareNode]): found nodes associated to path | 223 @return (list[ShareNode]): found nodes associated to path |
221 for node in node.itervalues(): | 234 for node in node.itervalues(): |
222 self._getSharedPaths(node, paths) | 235 self._getSharedPaths(node, paths) |
223 elif node.type == TYPE_PATH: | 236 elif node.type == TYPE_PATH: |
224 paths.setdefault(node.path, []).append(node) | 237 paths.setdefault(node.path, []).append(node) |
225 else: | 238 else: |
226 raise exceptions.InternalError(u'unknown node type: {type}'.format( | 239 raise exceptions.InternalError( |
227 type = node.type)) | 240 u"unknown node type: {type}".format(type=node.type) |
241 ) | |
228 | 242 |
229 def getSharedPaths(self): | 243 def getSharedPaths(self): |
230 """retrieve nodes by shared path | 244 """retrieve nodes by shared path |
231 | 245 |
232 this method will retrieve recursively shared path in children of this node | 246 this method will retrieve recursively shared path in children of this node |
233 @return (dict): map from shared path to list of nodes | 247 @return (dict): map from shared path to list of nodes |
234 """ | 248 """ |
235 if self.type == TYPE_PATH: | 249 if self.type == TYPE_PATH: |
236 raise exceptions.InternalError("getSharedPaths must be used on a virtual node") | 250 raise exceptions.InternalError( |
251 "getSharedPaths must be used on a virtual node" | |
252 ) | |
237 paths = {} | 253 paths = {} |
238 self._getSharedPaths(self, paths) | 254 self._getSharedPaths(self, paths) |
239 return paths | 255 return paths |
240 | 256 |
241 | 257 |
242 class XEP_0329(object): | 258 class XEP_0329(object): |
243 | |
244 def __init__(self, host): | 259 def __init__(self, host): |
245 log.info(_("File Information Sharing initialization")) | 260 log.info(_("File Information Sharing initialization")) |
246 self.host = host | 261 self.host = host |
247 ShareNode.host = host | 262 ShareNode.host = host |
248 self._h = host.plugins['XEP-0300'] | 263 self._h = host.plugins["XEP-0300"] |
249 self._jf = host.plugins['XEP-0234'] | 264 self._jf = host.plugins["XEP-0234"] |
250 host.bridge.addMethod("FISList", ".plugin", in_sign='ssa{ss}s', out_sign='aa{ss}', method=self._listFiles, async=True) | 265 host.bridge.addMethod( |
251 host.bridge.addMethod("FISLocalSharesGet", ".plugin", in_sign='s', out_sign='as', method=self._localSharesGet) | 266 "FISList", |
252 host.bridge.addMethod("FISSharePath", ".plugin", in_sign='ssss', out_sign='s', method=self._sharePath) | 267 ".plugin", |
253 host.bridge.addMethod("FISUnsharePath", ".plugin", in_sign='ss', out_sign='', method=self._unsharePath) | 268 in_sign="ssa{ss}s", |
254 host.bridge.addSignal("FISSharedPathNew", ".plugin", signature='sss') | 269 out_sign="aa{ss}", |
255 host.bridge.addSignal("FISSharedPathRemoved", ".plugin", signature='ss') | 270 method=self._listFiles, |
271 async=True, | |
272 ) | |
273 host.bridge.addMethod( | |
274 "FISLocalSharesGet", | |
275 ".plugin", | |
276 in_sign="s", | |
277 out_sign="as", | |
278 method=self._localSharesGet, | |
279 ) | |
280 host.bridge.addMethod( | |
281 "FISSharePath", | |
282 ".plugin", | |
283 in_sign="ssss", | |
284 out_sign="s", | |
285 method=self._sharePath, | |
286 ) | |
287 host.bridge.addMethod( | |
288 "FISUnsharePath", | |
289 ".plugin", | |
290 in_sign="ss", | |
291 out_sign="", | |
292 method=self._unsharePath, | |
293 ) | |
294 host.bridge.addSignal("FISSharedPathNew", ".plugin", signature="sss") | |
295 host.bridge.addSignal("FISSharedPathRemoved", ".plugin", signature="ss") | |
256 host.trigger.add("XEP-0234_fileSendingRequest", self._fileSendingRequestTrigger) | 296 host.trigger.add("XEP-0234_fileSendingRequest", self._fileSendingRequestTrigger) |
257 host.registerNamespace('fis', NS_FIS) | 297 host.registerNamespace("fis", NS_FIS) |
258 | 298 |
259 def getHandler(self, client): | 299 def getHandler(self, client): |
260 return XEP_0329_handler(self) | 300 return XEP_0329_handler(self) |
261 | 301 |
262 def profileConnected(self, client): | 302 def profileConnected(self, client): |
263 if not client.is_component: | 303 if not client.is_component: |
264 client._XEP_0329_root_node = ShareNode(None, None, TYPE_VIRTUAL, {C.ACCESS_PERM_READ: {KEY_TYPE: C.ACCESS_TYPE_PUBLIC}}) | 304 client._XEP_0329_root_node = ShareNode( |
265 client._XEP_0329_names_data = {} # name to share map | 305 None, |
266 | 306 None, |
267 def _fileSendingRequestTrigger(self, client, session, content_data, content_name, file_data, file_elt): | 307 TYPE_VIRTUAL, |
308 {C.ACCESS_PERM_READ: {KEY_TYPE: C.ACCESS_TYPE_PUBLIC}}, | |
309 ) | |
310 client._XEP_0329_names_data = {} # name to share map | |
311 | |
312 def _fileSendingRequestTrigger( | |
313 self, client, session, content_data, content_name, file_data, file_elt | |
314 ): | |
268 """this trigger check that a requested file is available, and fill suitable data if so | 315 """this trigger check that a requested file is available, and fill suitable data if so |
269 | 316 |
270 path and name are used to retrieve the file. If path is missing, we try our luck with known names | 317 path and name are used to retrieve the file. If path is missing, we try our luck with known names |
271 """ | 318 """ |
272 if client.is_component: | 319 if client.is_component: |
273 return True, None | 320 return True, None |
274 | 321 |
275 try: | 322 try: |
276 name = file_data[u'name'] | 323 name = file_data[u"name"] |
277 except KeyError: | 324 except KeyError: |
278 return True, None | 325 return True, None |
279 assert u'/' not in name | 326 assert u"/" not in name |
280 | 327 |
281 path = file_data.get(u'path') | 328 path = file_data.get(u"path") |
282 if path is not None: | 329 if path is not None: |
283 # we have a path, we can follow it to find node | 330 # we have a path, we can follow it to find node |
284 try: | 331 try: |
285 node, rem_path = ShareNode.find(client, path, session[u'peer_jid']) | 332 node, rem_path = ShareNode.find(client, path, session[u"peer_jid"]) |
286 except (exceptions.PermissionError, exceptions.NotFound): | 333 except (exceptions.PermissionError, exceptions.NotFound): |
287 # no file, or file not allowed, we continue normal workflow | 334 # no file, or file not allowed, we continue normal workflow |
288 return True, None | 335 return True, None |
289 except exceptions.DataError: | 336 except exceptions.DataError: |
290 log.warning(_(u'invalid path: {path}').format(path=path)) | 337 log.warning(_(u"invalid path: {path}").format(path=path)) |
291 return True, None | 338 return True, None |
292 | 339 |
293 if node.type == TYPE_VIRTUAL: | 340 if node.type == TYPE_VIRTUAL: |
294 # we have a virtual node, so name must link to a path node | 341 # we have a virtual node, so name must link to a path node |
295 try: | 342 try: |
298 return True, None | 345 return True, None |
299 elif node.type == TYPE_PATH: | 346 elif node.type == TYPE_PATH: |
300 # we have a path node, so we can retrieve the full path now | 347 # we have a path node, so we can retrieve the full path now |
301 path = os.path.join(node.path, rem_path, name) | 348 path = os.path.join(node.path, rem_path, name) |
302 else: | 349 else: |
303 raise exceptions.InternalError(u'unknown type: {type}'.format(type=node.type)) | 350 raise exceptions.InternalError( |
351 u"unknown type: {type}".format(type=node.type) | |
352 ) | |
304 if not os.path.exists(path): | 353 if not os.path.exists(path): |
305 return True, None | 354 return True, None |
306 size = os.path.getsize(path) | 355 size = os.path.getsize(path) |
307 else: | 356 else: |
308 # we don't have the path, we try to find the file by its name | 357 # we don't have the path, we try to find the file by its name |
310 name_data = client._XEP_0329_names_data[name] | 359 name_data = client._XEP_0329_names_data[name] |
311 except KeyError: | 360 except KeyError: |
312 return True, None | 361 return True, None |
313 | 362 |
314 for path, shared_file in name_data.iteritems(): | 363 for path, shared_file in name_data.iteritems(): |
315 if True: # FIXME: filters are here | 364 if True: # FIXME: filters are here |
316 break | 365 break |
317 else: | 366 else: |
318 return True, None | 367 return True, None |
319 parent_node = shared_file[u'parent'] | 368 parent_node = shared_file[u"parent"] |
320 if not parent_node.checkPermissions(client, session[u'peer_jid']): | 369 if not parent_node.checkPermissions(client, session[u"peer_jid"]): |
321 log.warning(_(u"{peer_jid} requested a file (s)he can't access [{profile}]").format( | 370 log.warning( |
322 peer_jid = session[u'peer_jid'], profile = client.profile)) | 371 _( |
372 u"{peer_jid} requested a file (s)he can't access [{profile}]" | |
373 ).format(peer_jid=session[u"peer_jid"], profile=client.profile) | |
374 ) | |
323 return True, None | 375 return True, None |
324 size = shared_file[u'size'] | 376 size = shared_file[u"size"] |
325 | 377 |
326 file_data[u'size'] = size | 378 file_data[u"size"] = size |
327 file_elt.addElement(u'size', content=unicode(size)) | 379 file_elt.addElement(u"size", content=unicode(size)) |
328 hash_algo = file_data[u'hash_algo'] = self._h.getDefaultAlgo() | 380 hash_algo = file_data[u"hash_algo"] = self._h.getDefaultAlgo() |
329 hasher = file_data[u'hash_hasher'] = self._h.getHasher(hash_algo) | 381 hasher = file_data[u"hash_hasher"] = self._h.getHasher(hash_algo) |
330 file_elt.addChild(self._h.buildHashUsedElt(hash_algo)) | 382 file_elt.addChild(self._h.buildHashUsedElt(hash_algo)) |
331 content_data['stream_object'] = stream.FileStreamObject( | 383 content_data["stream_object"] = stream.FileStreamObject( |
332 self.host, | 384 self.host, |
333 client, | 385 client, |
334 path, | 386 path, |
335 uid=self._jf.getProgressId(session, content_name), | 387 uid=self._jf.getProgressId(session, content_name), |
336 size=size, | 388 size=size, |
337 data_cb=lambda data: hasher.update(data), | 389 data_cb=lambda data: hasher.update(data), |
338 ) | 390 ) |
339 return False, True | 391 return False, True |
340 | 392 |
341 # common methods | 393 # common methods |
342 | 394 |
343 def _requestHandler(self, client, iq_elt, root_nodes_cb, files_from_node_cb): | 395 def _requestHandler(self, client, iq_elt, root_nodes_cb, files_from_node_cb): |
344 iq_elt.handled = True | 396 iq_elt.handled = True |
345 owner = jid.JID(iq_elt['from']).userhostJID() | 397 owner = jid.JID(iq_elt["from"]).userhostJID() |
346 node = iq_elt.query.getAttribute('node') | 398 node = iq_elt.query.getAttribute("node") |
347 if not node: | 399 if not node: |
348 d = defer.maybeDeferred(root_nodes_cb, client, iq_elt, owner) | 400 d = defer.maybeDeferred(root_nodes_cb, client, iq_elt, owner) |
349 else: | 401 else: |
350 d = defer.maybeDeferred(files_from_node_cb, client, iq_elt, owner, node) | 402 d = defer.maybeDeferred(files_from_node_cb, client, iq_elt, owner, node) |
351 d.addErrback(lambda failure_: log.error(_(u"error while retrieving files: {msg}").format(msg=failure_))) | 403 d.addErrback( |
404 lambda failure_: log.error( | |
405 _(u"error while retrieving files: {msg}").format(msg=failure_) | |
406 ) | |
407 ) | |
352 | 408 |
353 def _iqError(self, client, iq_elt, condition="item-not-found"): | 409 def _iqError(self, client, iq_elt, condition="item-not-found"): |
354 error_elt = jabber_error.StanzaError(condition).toResponse(iq_elt) | 410 error_elt = jabber_error.StanzaError(condition).toResponse(iq_elt) |
355 client.send(error_elt) | 411 client.send(error_elt) |
356 | 412 |
357 # client | 413 # client |
358 | 414 |
359 def _addPathData(self, client, query_elt, path, parent_node): | 415 def _addPathData(self, client, query_elt, path, parent_node): |
360 """Fill query_elt with files/directories found in path""" | 416 """Fill query_elt with files/directories found in path""" |
361 name = os.path.basename(path) | 417 name = os.path.basename(path) |
362 if os.path.isfile(path): | 418 if os.path.isfile(path): |
363 size = os.path.getsize(path) | 419 size = os.path.getsize(path) |
364 mime_type = mimetypes.guess_type(path, strict=False)[0] | 420 mime_type = mimetypes.guess_type(path, strict=False)[0] |
365 file_elt = self._jf.buildFileElement(name = name, | 421 file_elt = self._jf.buildFileElement( |
366 size = size, | 422 name=name, size=size, mime_type=mime_type, modified=os.path.getmtime(path) |
367 mime_type = mime_type, | 423 ) |
368 modified = os.path.getmtime(path)) | |
369 | 424 |
370 query_elt.addChild(file_elt) | 425 query_elt.addChild(file_elt) |
371 # we don't specify hash as it would be too resource intensive to calculate it for all files | 426 # we don't specify hash as it would be too resource intensive to calculate it for all files |
372 # we add file to name_data, so users can request it later | 427 # we add file to name_data, so users can request it later |
373 name_data = client._XEP_0329_names_data.setdefault(name, {}) | 428 name_data = client._XEP_0329_names_data.setdefault(name, {}) |
374 if path not in name_data: | 429 if path not in name_data: |
375 name_data[path] = {'size': size, | 430 name_data[path] = { |
376 'mime_type': mime_type, | 431 "size": size, |
377 'parent': parent_node} | 432 "mime_type": mime_type, |
433 "parent": parent_node, | |
434 } | |
378 else: | 435 else: |
379 # we have a directory | 436 # we have a directory |
380 directory_elt = query_elt.addElement('directory') | 437 directory_elt = query_elt.addElement("directory") |
381 directory_elt['name'] = name | 438 directory_elt["name"] = name |
382 | 439 |
383 def _pathNodeHandler(self, client, iq_elt, query_elt, node, path): | 440 def _pathNodeHandler(self, client, iq_elt, query_elt, node, path): |
384 """Fill query_elt for path nodes, i.e. physical directories""" | 441 """Fill query_elt for path nodes, i.e. physical directories""" |
385 path = os.path.join(node.path, path) | 442 path = os.path.join(node.path, path) |
386 | 443 |
388 # path may have been moved since it has been shared | 445 # path may have been moved since it has been shared |
389 return self._iqError(client, iq_elt) | 446 return self._iqError(client, iq_elt) |
390 elif os.path.isfile(path): | 447 elif os.path.isfile(path): |
391 self._addPathData(client, query_elt, path, node) | 448 self._addPathData(client, query_elt, path, node) |
392 else: | 449 else: |
393 for name in sorted(os.listdir(path.encode('utf-8')), key=lambda n: n.lower()): | 450 for name in sorted(os.listdir(path.encode("utf-8")), key=lambda n: n.lower()): |
394 try: | 451 try: |
395 name = name.decode('utf-8', 'strict') | 452 name = name.decode("utf-8", "strict") |
396 except UnicodeDecodeError as e: | 453 except UnicodeDecodeError as e: |
397 log.warning(_(u"ignoring invalid unicode name ({name}): {msg}").format( | 454 log.warning( |
398 name = name.decode('utf-8', 'replace'), | 455 _(u"ignoring invalid unicode name ({name}): {msg}").format( |
399 msg = e)) | 456 name=name.decode("utf-8", "replace"), msg=e |
457 ) | |
458 ) | |
400 continue | 459 continue |
401 full_path = os.path.join(path, name) | 460 full_path = os.path.join(path, name) |
402 self._addPathData(client, query_elt, full_path, node) | 461 self._addPathData(client, query_elt, full_path, node) |
403 | 462 |
404 def _virtualNodeHandler(self, client, peer_jid, iq_elt, query_elt, node): | 463 def _virtualNodeHandler(self, client, peer_jid, iq_elt, query_elt, node): |
406 for name, child_node in node.iteritems(): | 465 for name, child_node in node.iteritems(): |
407 if not child_node.checkPermissions(client, peer_jid, check_parents=False): | 466 if not child_node.checkPermissions(client, peer_jid, check_parents=False): |
408 continue | 467 continue |
409 node_type = child_node.type | 468 node_type = child_node.type |
410 if node_type == TYPE_VIRTUAL: | 469 if node_type == TYPE_VIRTUAL: |
411 directory_elt = query_elt.addElement('directory') | 470 directory_elt = query_elt.addElement("directory") |
412 directory_elt['name'] = name | 471 directory_elt["name"] = name |
413 elif node_type == TYPE_PATH: | 472 elif node_type == TYPE_PATH: |
414 self._addPathData(client, query_elt, child_node.path, child_node) | 473 self._addPathData(client, query_elt, child_node.path, child_node) |
415 else: | 474 else: |
416 raise exceptions.InternalError(_(u'unexpected type: {type}').format(type=node_type)) | 475 raise exceptions.InternalError( |
476 _(u"unexpected type: {type}").format(type=node_type) | |
477 ) | |
417 | 478 |
418 def _getRootNodesCb(self, client, iq_elt, owner): | 479 def _getRootNodesCb(self, client, iq_elt, owner): |
419 peer_jid = jid.JID(iq_elt['from']) | 480 peer_jid = jid.JID(iq_elt["from"]) |
420 iq_result_elt = xmlstream.toResponse(iq_elt, 'result') | 481 iq_result_elt = xmlstream.toResponse(iq_elt, "result") |
421 query_elt = iq_result_elt.addElement((NS_FIS, 'query')) | 482 query_elt = iq_result_elt.addElement((NS_FIS, "query")) |
422 for name, node in client._XEP_0329_root_node.iteritems(): | 483 for name, node in client._XEP_0329_root_node.iteritems(): |
423 if not node.checkPermissions(client, peer_jid, check_parents=False): | 484 if not node.checkPermissions(client, peer_jid, check_parents=False): |
424 continue | 485 continue |
425 directory_elt = query_elt.addElement('directory') | 486 directory_elt = query_elt.addElement("directory") |
426 directory_elt['name'] = name | 487 directory_elt["name"] = name |
427 client.send(iq_result_elt) | 488 client.send(iq_result_elt) |
428 | 489 |
429 def _getFilesFromNodeCb(self, client, iq_elt, owner, node_path): | 490 def _getFilesFromNodeCb(self, client, iq_elt, owner, node_path): |
430 """Main method to retrieve files/directories from a node_path""" | 491 """Main method to retrieve files/directories from a node_path""" |
431 peer_jid = jid.JID(iq_elt[u'from']) | 492 peer_jid = jid.JID(iq_elt[u"from"]) |
432 try: | 493 try: |
433 node, path = ShareNode.find(client, node_path, peer_jid) | 494 node, path = ShareNode.find(client, node_path, peer_jid) |
434 except (exceptions.PermissionError, exceptions.NotFound): | 495 except (exceptions.PermissionError, exceptions.NotFound): |
435 return self._iqError(client, iq_elt) | 496 return self._iqError(client, iq_elt) |
436 except exceptions.DataError: | 497 except exceptions.DataError: |
437 return self._iqError(client, iq_elt, condition='not-acceptable') | 498 return self._iqError(client, iq_elt, condition="not-acceptable") |
438 | 499 |
439 node_type = node.type | 500 node_type = node.type |
440 peer_jid = jid.JID(iq_elt['from']) | 501 peer_jid = jid.JID(iq_elt["from"]) |
441 iq_result_elt = xmlstream.toResponse(iq_elt, 'result') | 502 iq_result_elt = xmlstream.toResponse(iq_elt, "result") |
442 query_elt = iq_result_elt.addElement((NS_FIS, 'query')) | 503 query_elt = iq_result_elt.addElement((NS_FIS, "query")) |
443 query_elt[u'node'] = node_path | 504 query_elt[u"node"] = node_path |
444 | 505 |
445 # we now fill query_elt according to node_type | 506 # we now fill query_elt according to node_type |
446 if node_type == TYPE_PATH: | 507 if node_type == TYPE_PATH: |
447 # it's a physical path | 508 # it's a physical path |
448 self._pathNodeHandler(client, iq_elt, query_elt, node, path) | 509 self._pathNodeHandler(client, iq_elt, query_elt, node, path) |
449 elif node_type == TYPE_VIRTUAL: | 510 elif node_type == TYPE_VIRTUAL: |
450 assert not path | 511 assert not path |
451 self._virtualNodeHandler(client, peer_jid, iq_elt, query_elt, node) | 512 self._virtualNodeHandler(client, peer_jid, iq_elt, query_elt, node) |
452 else: | 513 else: |
453 raise exceptions.InternalError(_(u'unknown node type: {type}').format(type=node_type)) | 514 raise exceptions.InternalError( |
515 _(u"unknown node type: {type}").format(type=node_type) | |
516 ) | |
454 | 517 |
455 client.send(iq_result_elt) | 518 client.send(iq_result_elt) |
456 | 519 |
457 def onRequest(self, iq_elt, client): | 520 def onRequest(self, iq_elt, client): |
458 return self._requestHandler(client, iq_elt, self._getRootNodesCb, self._getFilesFromNodeCb) | 521 return self._requestHandler( |
522 client, iq_elt, self._getRootNodesCb, self._getFilesFromNodeCb | |
523 ) | |
459 | 524 |
460 # Component | 525 # Component |
461 | 526 |
462 @defer.inlineCallbacks | 527 @defer.inlineCallbacks |
463 def _compGetRootNodesCb(self, client, iq_elt, owner): | 528 def _compGetRootNodesCb(self, client, iq_elt, owner): |
464 peer_jid = jid.JID(iq_elt['from']) | 529 peer_jid = jid.JID(iq_elt["from"]) |
465 files_data = yield self.host.memory.getFiles(client, peer_jid=peer_jid, parent=u'', | 530 files_data = yield self.host.memory.getFiles( |
466 type_=C.FILE_TYPE_DIRECTORY, owner=owner) | 531 client, |
467 iq_result_elt = xmlstream.toResponse(iq_elt, 'result') | 532 peer_jid=peer_jid, |
468 query_elt = iq_result_elt.addElement((NS_FIS, 'query')) | 533 parent=u"", |
534 type_=C.FILE_TYPE_DIRECTORY, | |
535 owner=owner, | |
536 ) | |
537 iq_result_elt = xmlstream.toResponse(iq_elt, "result") | |
538 query_elt = iq_result_elt.addElement((NS_FIS, "query")) | |
469 for file_data in files_data: | 539 for file_data in files_data: |
470 name = file_data[u'name'] | 540 name = file_data[u"name"] |
471 directory_elt = query_elt.addElement(u'directory') | 541 directory_elt = query_elt.addElement(u"directory") |
472 directory_elt[u'name'] = name | 542 directory_elt[u"name"] = name |
473 client.send(iq_result_elt) | 543 client.send(iq_result_elt) |
474 | 544 |
475 @defer.inlineCallbacks | 545 @defer.inlineCallbacks |
476 def _compGetFilesFromNodeCb(self, client, iq_elt, owner, node_path): | 546 def _compGetFilesFromNodeCb(self, client, iq_elt, owner, node_path): |
477 """retrieve files from local files repository according to permissions | 547 """retrieve files from local files repository according to permissions |
478 | 548 |
479 result stanza is then built and sent to requestor | 549 result stanza is then built and sent to requestor |
480 @trigger XEP-0329_compGetFilesFromNode(client, iq_elt, owner, node_path, files_data): can be used to add data/elements | 550 @trigger XEP-0329_compGetFilesFromNode(client, iq_elt, owner, node_path, files_data): can be used to add data/elements |
481 """ | 551 """ |
482 peer_jid = jid.JID(iq_elt['from']) | 552 peer_jid = jid.JID(iq_elt["from"]) |
483 try: | 553 try: |
484 files_data = yield self.host.memory.getFiles(client, peer_jid=peer_jid, path=node_path, owner=owner) | 554 files_data = yield self.host.memory.getFiles( |
555 client, peer_jid=peer_jid, path=node_path, owner=owner | |
556 ) | |
485 except exceptions.NotFound: | 557 except exceptions.NotFound: |
486 self._iqError(client, iq_elt) | 558 self._iqError(client, iq_elt) |
487 return | 559 return |
488 iq_result_elt = xmlstream.toResponse(iq_elt, 'result') | 560 iq_result_elt = xmlstream.toResponse(iq_elt, "result") |
489 query_elt = iq_result_elt.addElement((NS_FIS, 'query')) | 561 query_elt = iq_result_elt.addElement((NS_FIS, "query")) |
490 query_elt[u'node'] = node_path | 562 query_elt[u"node"] = node_path |
491 if not self.host.trigger.point(u'XEP-0329_compGetFilesFromNode', client, iq_elt, owner, node_path, files_data): | 563 if not self.host.trigger.point( |
564 u"XEP-0329_compGetFilesFromNode", client, iq_elt, owner, node_path, files_data | |
565 ): | |
492 return | 566 return |
493 for file_data in files_data: | 567 for file_data in files_data: |
494 file_elt = self._jf.buildFileElementFromDict(file_data, | 568 file_elt = self._jf.buildFileElementFromDict( |
495 modified=file_data.get(u'modified', file_data[u'created'])) | 569 file_data, modified=file_data.get(u"modified", file_data[u"created"]) |
570 ) | |
496 query_elt.addChild(file_elt) | 571 query_elt.addChild(file_elt) |
497 client.send(iq_result_elt) | 572 client.send(iq_result_elt) |
498 | 573 |
499 def onComponentRequest(self, iq_elt, client): | 574 def onComponentRequest(self, iq_elt, client): |
500 return self._requestHandler(client, iq_elt, self._compGetRootNodesCb, self._compGetFilesFromNodeCb) | 575 return self._requestHandler( |
576 client, iq_elt, self._compGetRootNodesCb, self._compGetFilesFromNodeCb | |
577 ) | |
501 | 578 |
502 def _parseResult(self, iq_elt): | 579 def _parseResult(self, iq_elt): |
503 query_elt = next(iq_elt.elements(NS_FIS, 'query')) | 580 query_elt = next(iq_elt.elements(NS_FIS, "query")) |
504 files = [] | 581 files = [] |
505 | 582 |
506 for elt in query_elt.elements(): | 583 for elt in query_elt.elements(): |
507 if elt.name == 'file': | 584 if elt.name == "file": |
508 # we have a file | 585 # we have a file |
509 try: | 586 try: |
510 file_data = self._jf.parseFileElement(elt) | 587 file_data = self._jf.parseFileElement(elt) |
511 except exceptions.DataError: | 588 except exceptions.DataError: |
512 continue | 589 continue |
513 file_data[u'type'] = C.FILE_TYPE_FILE | 590 file_data[u"type"] = C.FILE_TYPE_FILE |
514 elif elt.name == 'directory' and elt.uri == NS_FIS: | 591 elif elt.name == "directory" and elt.uri == NS_FIS: |
515 # we have a directory | 592 # we have a directory |
516 | 593 |
517 file_data = {'name': elt['name'], 'type': C.FILE_TYPE_DIRECTORY} | 594 file_data = {"name": elt["name"], "type": C.FILE_TYPE_DIRECTORY} |
518 else: | 595 else: |
519 log.warning(_(u"unexpected element, ignoring: {elt}").format(elt=elt.toXml())) | 596 log.warning( |
597 _(u"unexpected element, ignoring: {elt}").format(elt=elt.toXml()) | |
598 ) | |
520 continue | 599 continue |
521 files.append(file_data) | 600 files.append(file_data) |
522 return files | 601 return files |
523 | 602 |
524 # file methods # | 603 # file methods # |
525 | 604 |
526 def _serializeData(self, files_data): | 605 def _serializeData(self, files_data): |
527 for file_data in files_data: | 606 for file_data in files_data: |
528 for key, value in file_data.iteritems(): | 607 for key, value in file_data.iteritems(): |
529 file_data[key] = json.dumps(value) if key in ('extra',) else unicode(value) | 608 file_data[key] = ( |
609 json.dumps(value) if key in ("extra",) else unicode(value) | |
610 ) | |
530 return files_data | 611 return files_data |
531 | 612 |
532 def _listFiles(self, target_jid, path, extra, profile): | 613 def _listFiles(self, target_jid, path, extra, profile): |
533 client = self.host.getClient(profile) | 614 client = self.host.getClient(profile) |
534 target_jid = client.jid.userhostJID() if not target_jid else jid.JID(target_jid) | 615 target_jid = client.jid.userhostJID() if not target_jid else jid.JID(target_jid) |
543 @param path(unicode, None): path to the directory containing shared files | 624 @param path(unicode, None): path to the directory containing shared files |
544 None to get root directories | 625 None to get root directories |
545 @param extra(dict, None): extra data | 626 @param extra(dict, None): extra data |
546 @return list(dict): shared files | 627 @return list(dict): shared files |
547 """ | 628 """ |
548 iq_elt = client.IQ('get') | 629 iq_elt = client.IQ("get") |
549 iq_elt['to'] = target_jid.full() | 630 iq_elt["to"] = target_jid.full() |
550 query_elt = iq_elt.addElement((NS_FIS, 'query')) | 631 query_elt = iq_elt.addElement((NS_FIS, "query")) |
551 if path: | 632 if path: |
552 query_elt['node'] = path | 633 query_elt["node"] = path |
553 d = iq_elt.send() | 634 d = iq_elt.send() |
554 d.addCallback(self._parseResult) | 635 d.addCallback(self._parseResult) |
555 return d | 636 return d |
556 | 637 |
557 def _localSharesGet(self, profile): | 638 def _localSharesGet(self, profile): |
561 def localSharesGet(self, client): | 642 def localSharesGet(self, client): |
562 return client._XEP_0329_root_node.getSharedPaths().keys() | 643 return client._XEP_0329_root_node.getSharedPaths().keys() |
563 | 644 |
564 def _sharePath(self, name, path, access, profile): | 645 def _sharePath(self, name, path, access, profile): |
565 client = self.host.getClient(profile) | 646 client = self.host.getClient(profile) |
566 access= json.loads(access) | 647 access = json.loads(access) |
567 return self.sharePath(client, name or None, path, access) | 648 return self.sharePath(client, name or None, path, access) |
568 | 649 |
569 def sharePath(self, client, name, path, access): | 650 def sharePath(self, client, name, path, access): |
570 if client.is_component: | 651 if client.is_component: |
571 raise exceptions.ClientTypeError | 652 raise exceptions.ClientTypeError |
572 if not os.path.exists(path): | 653 if not os.path.exists(path): |
573 raise ValueError(_(u"This path doesn't exist!")) | 654 raise ValueError(_(u"This path doesn't exist!")) |
574 if not path or not path.strip(u' /'): | 655 if not path or not path.strip(u" /"): |
575 raise ValueError(_(u"A path need to be specified")) | 656 raise ValueError(_(u"A path need to be specified")) |
576 if not isinstance(access, dict): | 657 if not isinstance(access, dict): |
577 raise ValueError(_(u'access must be a dict')) | 658 raise ValueError(_(u"access must be a dict")) |
578 | 659 |
579 node = client._XEP_0329_root_node | 660 node = client._XEP_0329_root_node |
580 node_type = TYPE_PATH | 661 node_type = TYPE_PATH |
581 if os.path.isfile(path): | 662 if os.path.isfile(path): |
582 # we have a single file, the workflow is diferrent as we store all single files in the same dir | 663 # we have a single file, the workflow is diferrent as we store all single files in the same dir |
583 node = node.getOrCreate(SINGLE_FILES_DIR) | 664 node = node.getOrCreate(SINGLE_FILES_DIR) |
584 | 665 |
585 if not name: | 666 if not name: |
586 name = os.path.basename(path.rstrip(u' /')) | 667 name = os.path.basename(path.rstrip(u" /")) |
587 if not name: | 668 if not name: |
588 raise exceptions.InternalError(_(u"Can't find a proper name")) | 669 raise exceptions.InternalError(_(u"Can't find a proper name")) |
589 | 670 |
590 if name in node or name == SINGLE_FILES_DIR: | 671 if name in node or name == SINGLE_FILES_DIR: |
591 idx = 1 | 672 idx = 1 |
592 new_name = name + '_' + unicode(idx) | 673 new_name = name + "_" + unicode(idx) |
593 while new_name in node: | 674 while new_name in node: |
594 idx += 1 | 675 idx += 1 |
595 new_name = name + '_' + unicode(idx) | 676 new_name = name + "_" + unicode(idx) |
596 name = new_name | 677 name = new_name |
597 log.info(_(u"A directory with this name is already shared, renamed to {new_name} [{profile}]".format( | 678 log.info( |
598 new_name=new_name, profile=client.profile))) | 679 _( |
680 u"A directory with this name is already shared, renamed to {new_name} [{profile}]".format( | |
681 new_name=new_name, profile=client.profile | |
682 ) | |
683 ) | |
684 ) | |
599 | 685 |
600 ShareNode(name=name, parent=node, type_=node_type, access=access, path=path) | 686 ShareNode(name=name, parent=node, type_=node_type, access=access, path=path) |
601 self.host.bridge.FISSharedPathNew(path, name, client.profile) | 687 self.host.bridge.FISSharedPathNew(path, name, client.profile) |
602 return name | 688 return name |
603 | 689 |
619 self.plugin_parent = plugin_parent | 705 self.plugin_parent = plugin_parent |
620 self.host = plugin_parent.host | 706 self.host = plugin_parent.host |
621 | 707 |
622 def connectionInitialized(self): | 708 def connectionInitialized(self): |
623 if self.parent.is_component: | 709 if self.parent.is_component: |
624 self.xmlstream.addObserver(IQ_FIS_REQUEST, self.plugin_parent.onComponentRequest, client=self.parent) | 710 self.xmlstream.addObserver( |
625 else: | 711 IQ_FIS_REQUEST, self.plugin_parent.onComponentRequest, client=self.parent |
626 self.xmlstream.addObserver(IQ_FIS_REQUEST, self.plugin_parent.onRequest, client=self.parent) | 712 ) |
627 | 713 else: |
628 def getDiscoInfo(self, requestor, target, nodeIdentifier=''): | 714 self.xmlstream.addObserver( |
715 IQ_FIS_REQUEST, self.plugin_parent.onRequest, client=self.parent | |
716 ) | |
717 | |
718 def getDiscoInfo(self, requestor, target, nodeIdentifier=""): | |
629 return [disco.DiscoFeature(NS_FIS)] | 719 return [disco.DiscoFeature(NS_FIS)] |
630 | 720 |
631 def getDiscoItems(self, requestor, target, nodeIdentifier=''): | 721 def getDiscoItems(self, requestor, target, nodeIdentifier=""): |
632 return [] | 722 return [] |