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 []