Mercurial > libervia-backend
comparison sat/plugins/plugin_xep_0329.py @ 3028:ab2696e34d29
Python 3 port:
/!\ this is a huge commit
/!\ starting from this commit, SàT is needs Python 3.6+
/!\ SàT maybe be instable or some feature may not work anymore, this will improve with time
This patch port backend, bridge and frontends to Python 3.
Roughly this has been done this way:
- 2to3 tools has been applied (with python 3.7)
- all references to python2 have been replaced with python3 (notably shebangs)
- fixed files not handled by 2to3 (notably the shell script)
- several manual fixes
- fixed issues reported by Python 3 that where not handled in Python 2
- replaced "async" with "async_" when needed (it's a reserved word from Python 3.7)
- replaced zope's "implements" with @implementer decorator
- temporary hack to handle data pickled in database, as str or bytes may be returned,
to be checked later
- fixed hash comparison for password
- removed some code which is not needed anymore with Python 3
- deactivated some code which needs to be checked (notably certificate validation)
- tested with jp, fixed reported issues until some basic commands worked
- ported Primitivus (after porting dependencies like urwid satext)
- more manual fixes
author | Goffi <goffi@goffi.org> |
---|---|
date | Tue, 13 Aug 2019 19:08:41 +0200 |
parents | db0890c9c7db |
children | 95e2fd14a761 |
comparison
equal
deleted
inserted
replaced
3027:ff5bcb12ae60 | 3028:ab2696e34d29 |
---|---|
1 #!/usr/bin/env python2 | 1 #!/usr/bin/env python3 |
2 # -*- coding: utf-8 -*- | 2 # -*- coding: utf-8 -*- |
3 | 3 |
4 # SAT plugin for File Information Sharing (XEP-0329) | 4 # SAT plugin for File Information Sharing (XEP-0329) |
5 # Copyright (C) 2009-2019 Jérôme Poisson (goffi@goffi.org) | 5 # Copyright (C) 2009-2019 Jérôme Poisson (goffi@goffi.org) |
6 | 6 |
24 | 24 |
25 log = getLogger(__name__) | 25 log = getLogger(__name__) |
26 from sat.tools import stream | 26 from sat.tools import stream |
27 from sat.tools.common import regex | 27 from sat.tools.common import regex |
28 from wokkel import disco, iwokkel | 28 from wokkel import disco, iwokkel |
29 from zope.interface import implements | 29 from zope.interface import implementer |
30 from twisted.words.protocols.jabber import xmlstream | 30 from twisted.words.protocols.jabber import xmlstream |
31 from twisted.words.protocols.jabber import jid | 31 from twisted.words.protocols.jabber import jid |
32 from twisted.words.protocols.jabber import error as jabber_error | 32 from twisted.words.protocols.jabber import error as jabber_error |
33 from twisted.internet import defer | 33 from twisted.internet import defer |
34 import mimetypes | 34 import mimetypes |
43 C.PI_MODES: C.PLUG_MODE_BOTH, | 43 C.PI_MODES: C.PLUG_MODE_BOTH, |
44 C.PI_PROTOCOLS: ["XEP-0329"], | 44 C.PI_PROTOCOLS: ["XEP-0329"], |
45 C.PI_DEPENDENCIES: ["XEP-0234", "XEP-0300", "XEP-0106"], | 45 C.PI_DEPENDENCIES: ["XEP-0234", "XEP-0300", "XEP-0106"], |
46 C.PI_MAIN: "XEP_0329", | 46 C.PI_MAIN: "XEP_0329", |
47 C.PI_HANDLER: "yes", | 47 C.PI_HANDLER: "yes", |
48 C.PI_DESCRIPTION: _(u"""Implementation of File Information Sharing"""), | 48 C.PI_DESCRIPTION: _("""Implementation of File Information Sharing"""), |
49 } | 49 } |
50 | 50 |
51 NS_FIS = "urn:xmpp:fis:0" | 51 NS_FIS = "urn:xmpp:fis:0" |
52 | 52 |
53 IQ_FIS_REQUEST = C.IQ_GET + '/query[@xmlns="' + NS_FIS + '"]' | 53 IQ_FIS_REQUEST = C.IQ_GET + '/query[@xmlns="' + NS_FIS + '"]' |
54 SINGLE_FILES_DIR = u"files" | 54 SINGLE_FILES_DIR = "files" |
55 TYPE_VIRTUAL = u"virtual" | 55 TYPE_VIRTUAL = "virtual" |
56 TYPE_PATH = u"path" | 56 TYPE_PATH = "path" |
57 SHARE_TYPES = (TYPE_PATH, TYPE_VIRTUAL) | 57 SHARE_TYPES = (TYPE_PATH, TYPE_VIRTUAL) |
58 KEY_TYPE = u"type" | 58 KEY_TYPE = "type" |
59 | 59 |
60 | 60 |
61 class ShareNode(object): | 61 class ShareNode(object): |
62 """Node containing directory or files to share, virtual or real""" | 62 """Node containing directory or files to share, virtual or real""" |
63 | 63 |
64 host = None | 64 host = None |
65 | 65 |
66 def __init__(self, name, parent, type_, access, path=None): | 66 def __init__(self, name, parent, type_, access, path=None): |
67 assert type_ in SHARE_TYPES | 67 assert type_ in SHARE_TYPES |
68 if name is not None: | 68 if name is not None: |
69 if name == u".." or u"/" in name or u"\\" in name: | 69 if name == ".." or "/" in name or "\\" in name: |
70 log.warning( | 70 log.warning( |
71 _(u"path change chars found in name [{name}], hack attempt?").format( | 71 _("path change chars found in name [{name}], hack attempt?").format( |
72 name=name | 72 name=name |
73 ) | 73 ) |
74 ) | 74 ) |
75 if name == u"..": | 75 if name == "..": |
76 name = u"--" | 76 name = "--" |
77 else: | 77 else: |
78 name = regex.pathEscape(name) | 78 name = regex.pathEscape(name) |
79 self.name = name | 79 self.name = name |
80 self.children = {} | 80 self.children = {} |
81 self.type = type_ | 81 self.type = type_ |
87 parent.addChild(self) | 87 parent.addChild(self) |
88 else: | 88 else: |
89 assert name is None | 89 assert name is None |
90 if path is not None: | 90 if path is not None: |
91 if type_ != TYPE_PATH: | 91 if type_ != TYPE_PATH: |
92 raise exceptions.InternalError(_(u"path can only be set on path nodes")) | 92 raise exceptions.InternalError(_("path can only be set on path nodes")) |
93 self._path = path | 93 self._path = path |
94 | 94 |
95 @property | 95 @property |
96 def path(self): | 96 def path(self): |
97 return self._path | 97 return self._path |
104 | 104 |
105 def __iter__(self): | 105 def __iter__(self): |
106 return self.children.__iter__() | 106 return self.children.__iter__() |
107 | 107 |
108 def iteritems(self): | 108 def iteritems(self): |
109 return self.children.iteritems() | 109 return iter(self.children.items()) |
110 | 110 |
111 def itervalues(self): | 111 def itervalues(self): |
112 return self.children.itervalues() | 112 return iter(self.children.values()) |
113 | 113 |
114 def getOrCreate(self, name, type_=TYPE_VIRTUAL, access=None): | 114 def getOrCreate(self, name, type_=TYPE_VIRTUAL, access=None): |
115 """Get a node or create a virtual node and return it""" | 115 """Get a node or create a virtual node and return it""" |
116 if access is None: | 116 if access is None: |
117 access = {C.ACCESS_PERM_READ: {KEY_TYPE: C.ACCESS_TYPE_PUBLIC}} | 117 access = {C.ACCESS_PERM_READ: {KEY_TYPE: C.ACCESS_TYPE_PUBLIC}} |
121 node = ShareNode(name, self, type_=type_, access=access) | 121 node = ShareNode(name, self, type_=type_, access=access) |
122 return node | 122 return node |
123 | 123 |
124 def addChild(self, node): | 124 def addChild(self, node): |
125 if node.parent is not None: | 125 if node.parent is not None: |
126 raise exceptions.ConflictError(_(u"a node can't have several parents")) | 126 raise exceptions.ConflictError(_("a node can't have several parents")) |
127 node.parent = self | 127 node.parent = self |
128 self.children[node.name] = node | 128 self.children[node.name] = node |
129 | 129 |
130 def removeFromParent(self): | 130 def removeFromParent(self): |
131 try: | 131 try: |
132 del self.parent.children[self.name] | 132 del self.parent.children[self.name] |
133 except TypeError: | 133 except TypeError: |
134 raise exceptions.InternalError( | 134 raise exceptions.InternalError( |
135 u"trying to remove a node from inexisting parent" | 135 "trying to remove a node from inexisting parent" |
136 ) | 136 ) |
137 except KeyError: | 137 except KeyError: |
138 raise exceptions.InternalError(u"node not found in parent's children") | 138 raise exceptions.InternalError("node not found in parent's children") |
139 self.parent = None | 139 self.parent = None |
140 | 140 |
141 def _checkNodePermission(self, client, node, perms, peer_jid): | 141 def _checkNodePermission(self, client, node, perms, peer_jid): |
142 """Check access to this node for peer_jid | 142 """Check access to this node for peer_jid |
143 | 143 |
144 @param node(SharedNode): node to check access | 144 @param node(SharedNode): node to check access |
145 @param perms(unicode): permissions to check, iterable of C.ACCESS_PERM_* | 145 @param perms(unicode): permissions to check, iterable of C.ACCESS_PERM_* |
146 @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 |
147 @return (bool): True if entity can access | 147 @return (bool): True if entity can access |
148 """ | 148 """ |
149 file_data = {u"access": self.access, u"owner": client.jid.userhostJID()} | 149 file_data = {"access": self.access, "owner": client.jid.userhostJID()} |
150 try: | 150 try: |
151 self.host.memory.checkFilePermission(file_data, peer_jid, perms) | 151 self.host.memory.checkFilePermission(file_data, peer_jid, perms) |
152 except exceptions.PermissionError: | 152 except exceptions.PermissionError: |
153 return False | 153 return False |
154 else: | 154 else: |
187 @return (dict, unicode): shared data, remaining path | 187 @return (dict, unicode): shared data, remaining path |
188 @raise exceptions.PermissionError: user can't access this file | 188 @raise exceptions.PermissionError: user can't access this file |
189 @raise exceptions.DataError: path is invalid | 189 @raise exceptions.DataError: path is invalid |
190 @raise NotFound: path lead to a non existing file/directory | 190 @raise NotFound: path lead to a non existing file/directory |
191 """ | 191 """ |
192 path_elts = filter(None, path.split(u"/")) | 192 path_elts = [_f for _f in path.split("/") if _f] |
193 | 193 |
194 if u".." in path_elts: | 194 if ".." in path_elts: |
195 log.warning(_( | 195 log.warning(_( |
196 u'parent dir ("..") found in path, hack attempt? path is {path} ' | 196 'parent dir ("..") found in path, hack attempt? path is {path} ' |
197 u'[{profile}]').format(path=path, profile=client.profile)) | 197 '[{profile}]').format(path=path, profile=client.profile)) |
198 raise exceptions.PermissionError(u"illegal path elements") | 198 raise exceptions.PermissionError("illegal path elements") |
199 | 199 |
200 if not path_elts: | 200 if not path_elts: |
201 raise exceptions.DataError(_(u"path is invalid: {path}").format(path=path)) | 201 raise exceptions.DataError(_("path is invalid: {path}").format(path=path)) |
202 | 202 |
203 node = client._XEP_0329_root_node | 203 node = client._XEP_0329_root_node |
204 | 204 |
205 while path_elts: | 205 while path_elts: |
206 if node.type == TYPE_VIRTUAL: | 206 if node.type == TYPE_VIRTUAL: |
210 raise exceptions.NotFound | 210 raise exceptions.NotFound |
211 elif node.type == TYPE_PATH: | 211 elif node.type == TYPE_PATH: |
212 break | 212 break |
213 | 213 |
214 if not node.checkPermissions(client, peer_jid, perms=perms): | 214 if not node.checkPermissions(client, peer_jid, perms=perms): |
215 raise exceptions.PermissionError(u"permission denied") | 215 raise exceptions.PermissionError("permission denied") |
216 | 216 |
217 return node, u"/".join(path_elts) | 217 return node, "/".join(path_elts) |
218 | 218 |
219 def findByLocalPath(self, path): | 219 def findByLocalPath(self, path): |
220 """retrieve nodes linking to local path | 220 """retrieve nodes linking to local path |
221 | 221 |
222 @return (list[ShareNode]): found nodes associated to path | 222 @return (list[ShareNode]): found nodes associated to path |
228 except KeyError: | 228 except KeyError: |
229 raise exceptions.NotFound | 229 raise exceptions.NotFound |
230 | 230 |
231 def _getSharedPaths(self, node, paths): | 231 def _getSharedPaths(self, node, paths): |
232 if node.type == TYPE_VIRTUAL: | 232 if node.type == TYPE_VIRTUAL: |
233 for node in node.itervalues(): | 233 for node in node.values(): |
234 self._getSharedPaths(node, paths) | 234 self._getSharedPaths(node, paths) |
235 elif node.type == TYPE_PATH: | 235 elif node.type == TYPE_PATH: |
236 paths.setdefault(node.path, []).append(node) | 236 paths.setdefault(node.path, []).append(node) |
237 else: | 237 else: |
238 raise exceptions.InternalError( | 238 raise exceptions.InternalError( |
239 u"unknown node type: {type}".format(type=node.type) | 239 "unknown node type: {type}".format(type=node.type) |
240 ) | 240 ) |
241 | 241 |
242 def getSharedPaths(self): | 242 def getSharedPaths(self): |
243 """retrieve nodes by shared path | 243 """retrieve nodes by shared path |
244 | 244 |
265 "FISList", | 265 "FISList", |
266 ".plugin", | 266 ".plugin", |
267 in_sign="ssa{ss}s", | 267 in_sign="ssa{ss}s", |
268 out_sign="aa{ss}", | 268 out_sign="aa{ss}", |
269 method=self._listFiles, | 269 method=self._listFiles, |
270 async=True, | 270 async_=True, |
271 ) | 271 ) |
272 host.bridge.addMethod( | 272 host.bridge.addMethod( |
273 "FISLocalSharesGet", | 273 "FISLocalSharesGet", |
274 ".plugin", | 274 ".plugin", |
275 in_sign="s", | 275 in_sign="s", |
318 """ | 318 """ |
319 if client.is_component: | 319 if client.is_component: |
320 return True, None | 320 return True, None |
321 | 321 |
322 try: | 322 try: |
323 name = file_data[u"name"] | 323 name = file_data["name"] |
324 except KeyError: | 324 except KeyError: |
325 return True, None | 325 return True, None |
326 assert u"/" not in name | 326 assert "/" not in name |
327 | 327 |
328 path = file_data.get(u"path") | 328 path = file_data.get("path") |
329 if path is not None: | 329 if path is not None: |
330 # we have a path, we can follow it to find node | 330 # we have a path, we can follow it to find node |
331 try: | 331 try: |
332 node, rem_path = ShareNode.find(client, path, session[u"peer_jid"]) | 332 node, rem_path = ShareNode.find(client, path, session["peer_jid"]) |
333 except (exceptions.PermissionError, exceptions.NotFound): | 333 except (exceptions.PermissionError, exceptions.NotFound): |
334 # no file, or file not allowed, we continue normal workflow | 334 # no file, or file not allowed, we continue normal workflow |
335 return True, None | 335 return True, None |
336 except exceptions.DataError: | 336 except exceptions.DataError: |
337 log.warning(_(u"invalid path: {path}").format(path=path)) | 337 log.warning(_("invalid path: {path}").format(path=path)) |
338 return True, None | 338 return True, None |
339 | 339 |
340 if node.type == TYPE_VIRTUAL: | 340 if node.type == TYPE_VIRTUAL: |
341 # 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 |
342 try: | 342 try: |
346 elif node.type == TYPE_PATH: | 346 elif node.type == TYPE_PATH: |
347 # 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 |
348 path = os.path.join(node.path, rem_path, name) | 348 path = os.path.join(node.path, rem_path, name) |
349 else: | 349 else: |
350 raise exceptions.InternalError( | 350 raise exceptions.InternalError( |
351 u"unknown type: {type}".format(type=node.type) | 351 "unknown type: {type}".format(type=node.type) |
352 ) | 352 ) |
353 if not os.path.exists(path): | 353 if not os.path.exists(path): |
354 return True, None | 354 return True, None |
355 size = os.path.getsize(path) | 355 size = os.path.getsize(path) |
356 else: | 356 else: |
358 try: | 358 try: |
359 name_data = client._XEP_0329_names_data[name] | 359 name_data = client._XEP_0329_names_data[name] |
360 except KeyError: | 360 except KeyError: |
361 return True, None | 361 return True, None |
362 | 362 |
363 for path, shared_file in name_data.iteritems(): | 363 for path, shared_file in name_data.items(): |
364 if True: # FIXME: filters are here | 364 if True: # FIXME: filters are here |
365 break | 365 break |
366 else: | 366 else: |
367 return True, None | 367 return True, None |
368 parent_node = shared_file[u"parent"] | 368 parent_node = shared_file["parent"] |
369 if not parent_node.checkPermissions(client, session[u"peer_jid"]): | 369 if not parent_node.checkPermissions(client, session["peer_jid"]): |
370 log.warning( | 370 log.warning( |
371 _( | 371 _( |
372 u"{peer_jid} requested a file (s)he can't access [{profile}]" | 372 "{peer_jid} requested a file (s)he can't access [{profile}]" |
373 ).format(peer_jid=session[u"peer_jid"], profile=client.profile) | 373 ).format(peer_jid=session["peer_jid"], profile=client.profile) |
374 ) | 374 ) |
375 return True, None | 375 return True, None |
376 size = shared_file[u"size"] | 376 size = shared_file["size"] |
377 | 377 |
378 file_data[u"size"] = size | 378 file_data["size"] = size |
379 file_elt.addElement(u"size", content=unicode(size)) | 379 file_elt.addElement("size", content=str(size)) |
380 hash_algo = file_data[u"hash_algo"] = self._h.getDefaultAlgo() | 380 hash_algo = file_data["hash_algo"] = self._h.getDefaultAlgo() |
381 hasher = file_data[u"hash_hasher"] = self._h.getHasher(hash_algo) | 381 hasher = file_data["hash_hasher"] = self._h.getHasher(hash_algo) |
382 file_elt.addChild(self._h.buildHashUsedElt(hash_algo)) | 382 file_elt.addChild(self._h.buildHashUsedElt(hash_algo)) |
383 content_data["stream_object"] = stream.FileStreamObject( | 383 content_data["stream_object"] = stream.FileStreamObject( |
384 self.host, | 384 self.host, |
385 client, | 385 client, |
386 path, | 386 path, |
399 d = defer.maybeDeferred(root_nodes_cb, client, iq_elt) | 399 d = defer.maybeDeferred(root_nodes_cb, client, iq_elt) |
400 else: | 400 else: |
401 d = defer.maybeDeferred(files_from_node_cb, client, iq_elt, node) | 401 d = defer.maybeDeferred(files_from_node_cb, client, iq_elt, node) |
402 d.addErrback( | 402 d.addErrback( |
403 lambda failure_: log.error( | 403 lambda failure_: log.error( |
404 _(u"error while retrieving files: {msg}").format(msg=failure_) | 404 _("error while retrieving files: {msg}").format(msg=failure_) |
405 ) | 405 ) |
406 ) | 406 ) |
407 | 407 |
408 def _iqError(self, client, iq_elt, condition="item-not-found"): | 408 def _iqError(self, client, iq_elt, condition="item-not-found"): |
409 error_elt = jabber_error.StanzaError(condition).toResponse(iq_elt) | 409 error_elt = jabber_error.StanzaError(condition).toResponse(iq_elt) |
450 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()): |
451 try: | 451 try: |
452 name = name.decode("utf-8", "strict") | 452 name = name.decode("utf-8", "strict") |
453 except UnicodeDecodeError as e: | 453 except UnicodeDecodeError as e: |
454 log.warning( | 454 log.warning( |
455 _(u"ignoring invalid unicode name ({name}): {msg}").format( | 455 _("ignoring invalid unicode name ({name}): {msg}").format( |
456 name=name.decode("utf-8", "replace"), msg=e | 456 name=name.decode("utf-8", "replace"), msg=e |
457 ) | 457 ) |
458 ) | 458 ) |
459 continue | 459 continue |
460 full_path = os.path.join(path, name) | 460 full_path = os.path.join(path, name) |
461 self._addPathData(client, query_elt, full_path, node) | 461 self._addPathData(client, query_elt, full_path, node) |
462 | 462 |
463 def _virtualNodeHandler(self, client, peer_jid, iq_elt, query_elt, node): | 463 def _virtualNodeHandler(self, client, peer_jid, iq_elt, query_elt, node): |
464 """Fill query_elt for virtual nodes""" | 464 """Fill query_elt for virtual nodes""" |
465 for name, child_node in node.iteritems(): | 465 for name, child_node in node.items(): |
466 if not child_node.checkPermissions(client, peer_jid, check_parents=False): | 466 if not child_node.checkPermissions(client, peer_jid, check_parents=False): |
467 continue | 467 continue |
468 node_type = child_node.type | 468 node_type = child_node.type |
469 if node_type == TYPE_VIRTUAL: | 469 if node_type == TYPE_VIRTUAL: |
470 directory_elt = query_elt.addElement("directory") | 470 directory_elt = query_elt.addElement("directory") |
471 directory_elt["name"] = name | 471 directory_elt["name"] = name |
472 elif node_type == TYPE_PATH: | 472 elif node_type == TYPE_PATH: |
473 self._addPathData(client, query_elt, child_node.path, child_node) | 473 self._addPathData(client, query_elt, child_node.path, child_node) |
474 else: | 474 else: |
475 raise exceptions.InternalError( | 475 raise exceptions.InternalError( |
476 _(u"unexpected type: {type}").format(type=node_type) | 476 _("unexpected type: {type}").format(type=node_type) |
477 ) | 477 ) |
478 | 478 |
479 def _getRootNodesCb(self, client, iq_elt): | 479 def _getRootNodesCb(self, client, iq_elt): |
480 peer_jid = jid.JID(iq_elt["from"]) | 480 peer_jid = jid.JID(iq_elt["from"]) |
481 iq_result_elt = xmlstream.toResponse(iq_elt, "result") | 481 iq_result_elt = xmlstream.toResponse(iq_elt, "result") |
482 query_elt = iq_result_elt.addElement((NS_FIS, "query")) | 482 query_elt = iq_result_elt.addElement((NS_FIS, "query")) |
483 for name, node in client._XEP_0329_root_node.iteritems(): | 483 for name, node in client._XEP_0329_root_node.items(): |
484 if not node.checkPermissions(client, peer_jid, check_parents=False): | 484 if not node.checkPermissions(client, peer_jid, check_parents=False): |
485 continue | 485 continue |
486 directory_elt = query_elt.addElement("directory") | 486 directory_elt = query_elt.addElement("directory") |
487 directory_elt["name"] = name | 487 directory_elt["name"] = name |
488 client.send(iq_result_elt) | 488 client.send(iq_result_elt) |
489 | 489 |
490 def _getFilesFromNodeCb(self, client, iq_elt, node_path): | 490 def _getFilesFromNodeCb(self, client, iq_elt, node_path): |
491 """Main method to retrieve files/directories from a node_path""" | 491 """Main method to retrieve files/directories from a node_path""" |
492 peer_jid = jid.JID(iq_elt[u"from"]) | 492 peer_jid = jid.JID(iq_elt["from"]) |
493 try: | 493 try: |
494 node, path = ShareNode.find(client, node_path, peer_jid) | 494 node, path = ShareNode.find(client, node_path, peer_jid) |
495 except (exceptions.PermissionError, exceptions.NotFound): | 495 except (exceptions.PermissionError, exceptions.NotFound): |
496 return self._iqError(client, iq_elt) | 496 return self._iqError(client, iq_elt) |
497 except exceptions.DataError: | 497 except exceptions.DataError: |
499 | 499 |
500 node_type = node.type | 500 node_type = node.type |
501 peer_jid = jid.JID(iq_elt["from"]) | 501 peer_jid = jid.JID(iq_elt["from"]) |
502 iq_result_elt = xmlstream.toResponse(iq_elt, "result") | 502 iq_result_elt = xmlstream.toResponse(iq_elt, "result") |
503 query_elt = iq_result_elt.addElement((NS_FIS, "query")) | 503 query_elt = iq_result_elt.addElement((NS_FIS, "query")) |
504 query_elt[u"node"] = node_path | 504 query_elt["node"] = node_path |
505 | 505 |
506 # we now fill query_elt according to node_type | 506 # we now fill query_elt according to node_type |
507 if node_type == TYPE_PATH: | 507 if node_type == TYPE_PATH: |
508 # it's a physical path | 508 # it's a physical path |
509 self._pathNodeHandler(client, iq_elt, query_elt, node, path) | 509 self._pathNodeHandler(client, iq_elt, query_elt, node, path) |
510 elif node_type == TYPE_VIRTUAL: | 510 elif node_type == TYPE_VIRTUAL: |
511 assert not path | 511 assert not path |
512 self._virtualNodeHandler(client, peer_jid, iq_elt, query_elt, node) | 512 self._virtualNodeHandler(client, peer_jid, iq_elt, query_elt, node) |
513 else: | 513 else: |
514 raise exceptions.InternalError( | 514 raise exceptions.InternalError( |
515 _(u"unknown node type: {type}").format(type=node_type) | 515 _("unknown node type: {type}").format(type=node_type) |
516 ) | 516 ) |
517 | 517 |
518 client.send(iq_result_elt) | 518 client.send(iq_result_elt) |
519 | 519 |
520 def onRequest(self, iq_elt, client): | 520 def onRequest(self, iq_elt, client): |
531 @return (tuple[jid.JID, jid.JID]): peer_jid and owner | 531 @return (tuple[jid.JID, jid.JID]): peer_jid and owner |
532 """ | 532 """ |
533 to_jid = jid.JID(iq_elt['to']) | 533 to_jid = jid.JID(iq_elt['to']) |
534 if to_jid.user: | 534 if to_jid.user: |
535 user = self.host.plugins['XEP-0106'].unescape(to_jid.user) | 535 user = self.host.plugins['XEP-0106'].unescape(to_jid.user) |
536 if u'@' in user: | 536 if '@' in user: |
537 # a full jid is specified | 537 # a full jid is specified |
538 owner = jid.JID(user) | 538 owner = jid.JID(user) |
539 else: | 539 else: |
540 # only user part is specified, we use our own host to build the full jid | 540 # only user part is specified, we use our own host to build the full jid |
541 owner = jid.JID(None, (user, client.host, None)) | 541 owner = jid.JID(None, (user, client.host, None)) |
549 def _compGetRootNodesCb(self, client, iq_elt): | 549 def _compGetRootNodesCb(self, client, iq_elt): |
550 peer_jid, owner = self._compParseJids(client, iq_elt) | 550 peer_jid, owner = self._compParseJids(client, iq_elt) |
551 files_data = yield self.host.memory.getFiles( | 551 files_data = yield self.host.memory.getFiles( |
552 client, | 552 client, |
553 peer_jid=peer_jid, | 553 peer_jid=peer_jid, |
554 parent=u"", | 554 parent="", |
555 type_=C.FILE_TYPE_DIRECTORY, | 555 type_=C.FILE_TYPE_DIRECTORY, |
556 owner=owner, | 556 owner=owner, |
557 ) | 557 ) |
558 iq_result_elt = xmlstream.toResponse(iq_elt, "result") | 558 iq_result_elt = xmlstream.toResponse(iq_elt, "result") |
559 query_elt = iq_result_elt.addElement((NS_FIS, "query")) | 559 query_elt = iq_result_elt.addElement((NS_FIS, "query")) |
560 for file_data in files_data: | 560 for file_data in files_data: |
561 name = file_data[u"name"] | 561 name = file_data["name"] |
562 directory_elt = query_elt.addElement(u"directory") | 562 directory_elt = query_elt.addElement("directory") |
563 directory_elt[u"name"] = name | 563 directory_elt["name"] = name |
564 client.send(iq_result_elt) | 564 client.send(iq_result_elt) |
565 | 565 |
566 @defer.inlineCallbacks | 566 @defer.inlineCallbacks |
567 def _compGetFilesFromNodeCb(self, client, iq_elt, node_path): | 567 def _compGetFilesFromNodeCb(self, client, iq_elt, node_path): |
568 """Retrieve files from local files repository according to permissions | 568 """Retrieve files from local files repository according to permissions |
582 return | 582 return |
583 except exceptions.PermissionError: | 583 except exceptions.PermissionError: |
584 self._iqError(client, iq_elt, condition='not-allowed') | 584 self._iqError(client, iq_elt, condition='not-allowed') |
585 return | 585 return |
586 except Exception as e: | 586 except Exception as e: |
587 log.error(u"internal server error: {e}".format(e=e)) | 587 log.error("internal server error: {e}".format(e=e)) |
588 self._iqError(client, iq_elt, condition='internal-server-error') | 588 self._iqError(client, iq_elt, condition='internal-server-error') |
589 return | 589 return |
590 iq_result_elt = xmlstream.toResponse(iq_elt, "result") | 590 iq_result_elt = xmlstream.toResponse(iq_elt, "result") |
591 query_elt = iq_result_elt.addElement((NS_FIS, "query")) | 591 query_elt = iq_result_elt.addElement((NS_FIS, "query")) |
592 query_elt[u"node"] = node_path | 592 query_elt["node"] = node_path |
593 if not self.host.trigger.point( | 593 if not self.host.trigger.point( |
594 u"XEP-0329_compGetFilesFromNode", client, iq_elt, owner, node_path, files_data | 594 "XEP-0329_compGetFilesFromNode", client, iq_elt, owner, node_path, files_data |
595 ): | 595 ): |
596 return | 596 return |
597 for file_data in files_data: | 597 for file_data in files_data: |
598 file_elt = self._jf.buildFileElementFromDict( | 598 file_elt = self._jf.buildFileElementFromDict( |
599 file_data, modified=file_data.get(u"modified", file_data[u"created"]) | 599 file_data, modified=file_data.get("modified", file_data["created"]) |
600 ) | 600 ) |
601 query_elt.addChild(file_elt) | 601 query_elt.addChild(file_elt) |
602 client.send(iq_result_elt) | 602 client.send(iq_result_elt) |
603 | 603 |
604 def onComponentRequest(self, iq_elt, client): | 604 def onComponentRequest(self, iq_elt, client): |
615 # we have a file | 615 # we have a file |
616 try: | 616 try: |
617 file_data = self._jf.parseFileElement(elt) | 617 file_data = self._jf.parseFileElement(elt) |
618 except exceptions.DataError: | 618 except exceptions.DataError: |
619 continue | 619 continue |
620 file_data[u"type"] = C.FILE_TYPE_FILE | 620 file_data["type"] = C.FILE_TYPE_FILE |
621 elif elt.name == "directory" and elt.uri == NS_FIS: | 621 elif elt.name == "directory" and elt.uri == NS_FIS: |
622 # we have a directory | 622 # we have a directory |
623 | 623 |
624 file_data = {"name": elt["name"], "type": C.FILE_TYPE_DIRECTORY} | 624 file_data = {"name": elt["name"], "type": C.FILE_TYPE_DIRECTORY} |
625 else: | 625 else: |
626 log.warning( | 626 log.warning( |
627 _(u"unexpected element, ignoring: {elt}").format(elt=elt.toXml()) | 627 _("unexpected element, ignoring: {elt}").format(elt=elt.toXml()) |
628 ) | 628 ) |
629 continue | 629 continue |
630 files.append(file_data) | 630 files.append(file_data) |
631 return files | 631 return files |
632 | 632 |
633 # file methods # | 633 # file methods # |
634 | 634 |
635 def _serializeData(self, files_data): | 635 def _serializeData(self, files_data): |
636 for file_data in files_data: | 636 for file_data in files_data: |
637 for key, value in file_data.iteritems(): | 637 for key, value in file_data.items(): |
638 file_data[key] = ( | 638 file_data[key] = ( |
639 json.dumps(value) if key in ("extra",) else unicode(value) | 639 json.dumps(value) if key in ("extra",) else str(value) |
640 ) | 640 ) |
641 return files_data | 641 return files_data |
642 | 642 |
643 def _listFiles(self, target_jid, path, extra, profile): | 643 def _listFiles(self, target_jid, path, extra, profile): |
644 client = self.host.getClient(profile) | 644 client = self.host.getClient(profile) |
668 def _localSharesGet(self, profile): | 668 def _localSharesGet(self, profile): |
669 client = self.host.getClient(profile) | 669 client = self.host.getClient(profile) |
670 return self.localSharesGet(client) | 670 return self.localSharesGet(client) |
671 | 671 |
672 def localSharesGet(self, client): | 672 def localSharesGet(self, client): |
673 return client._XEP_0329_root_node.getSharedPaths().keys() | 673 return list(client._XEP_0329_root_node.getSharedPaths().keys()) |
674 | 674 |
675 def _sharePath(self, name, path, access, profile): | 675 def _sharePath(self, name, path, access, profile): |
676 client = self.host.getClient(profile) | 676 client = self.host.getClient(profile) |
677 access = json.loads(access) | 677 access = json.loads(access) |
678 return self.sharePath(client, name or None, path, access) | 678 return self.sharePath(client, name or None, path, access) |
679 | 679 |
680 def sharePath(self, client, name, path, access): | 680 def sharePath(self, client, name, path, access): |
681 if client.is_component: | 681 if client.is_component: |
682 raise exceptions.ClientTypeError | 682 raise exceptions.ClientTypeError |
683 if not os.path.exists(path): | 683 if not os.path.exists(path): |
684 raise ValueError(_(u"This path doesn't exist!")) | 684 raise ValueError(_("This path doesn't exist!")) |
685 if not path or not path.strip(u" /"): | 685 if not path or not path.strip(" /"): |
686 raise ValueError(_(u"A path need to be specified")) | 686 raise ValueError(_("A path need to be specified")) |
687 if not isinstance(access, dict): | 687 if not isinstance(access, dict): |
688 raise ValueError(_(u"access must be a dict")) | 688 raise ValueError(_("access must be a dict")) |
689 | 689 |
690 node = client._XEP_0329_root_node | 690 node = client._XEP_0329_root_node |
691 node_type = TYPE_PATH | 691 node_type = TYPE_PATH |
692 if os.path.isfile(path): | 692 if os.path.isfile(path): |
693 # we have a single file, the workflow is diferrent as we store all single | 693 # we have a single file, the workflow is diferrent as we store all single |
694 # files in the same dir | 694 # files in the same dir |
695 node = node.getOrCreate(SINGLE_FILES_DIR) | 695 node = node.getOrCreate(SINGLE_FILES_DIR) |
696 | 696 |
697 if not name: | 697 if not name: |
698 name = os.path.basename(path.rstrip(u" /")) | 698 name = os.path.basename(path.rstrip(" /")) |
699 if not name: | 699 if not name: |
700 raise exceptions.InternalError(_(u"Can't find a proper name")) | 700 raise exceptions.InternalError(_("Can't find a proper name")) |
701 | 701 |
702 if name in node or name == SINGLE_FILES_DIR: | 702 if name in node or name == SINGLE_FILES_DIR: |
703 idx = 1 | 703 idx = 1 |
704 new_name = name + "_" + unicode(idx) | 704 new_name = name + "_" + str(idx) |
705 while new_name in node: | 705 while new_name in node: |
706 idx += 1 | 706 idx += 1 |
707 new_name = name + "_" + unicode(idx) | 707 new_name = name + "_" + str(idx) |
708 name = new_name | 708 name = new_name |
709 log.info(_( | 709 log.info(_( |
710 u"A directory with this name is already shared, renamed to {new_name} " | 710 "A directory with this name is already shared, renamed to {new_name} " |
711 u"[{profile}]".format( new_name=new_name, profile=client.profile))) | 711 "[{profile}]".format( new_name=new_name, profile=client.profile))) |
712 | 712 |
713 ShareNode(name=name, parent=node, type_=node_type, access=access, path=path) | 713 ShareNode(name=name, parent=node, type_=node_type, access=access, path=path) |
714 self.host.bridge.FISSharedPathNew(path, name, client.profile) | 714 self.host.bridge.FISSharedPathNew(path, name, client.profile) |
715 return name | 715 return name |
716 | 716 |
723 for node in nodes: | 723 for node in nodes: |
724 node.removeFromParent() | 724 node.removeFromParent() |
725 self.host.bridge.FISSharedPathRemoved(path, client.profile) | 725 self.host.bridge.FISSharedPathRemoved(path, client.profile) |
726 | 726 |
727 | 727 |
728 @implementer(iwokkel.IDisco) | |
728 class XEP_0329_handler(xmlstream.XMPPHandler): | 729 class XEP_0329_handler(xmlstream.XMPPHandler): |
729 implements(iwokkel.IDisco) | |
730 | 730 |
731 def __init__(self, plugin_parent): | 731 def __init__(self, plugin_parent): |
732 self.plugin_parent = plugin_parent | 732 self.plugin_parent = plugin_parent |
733 self.host = plugin_parent.host | 733 self.host = plugin_parent.host |
734 | 734 |