comparison sat/plugins/plugin_xep_0329.py @ 2589:282d1314d574

plugin XEP-0329: new methods/signals to handle shares: 2 new methods: - FISLocalSharesGet retrieve paths shared locally - FISUnsharePath stop sharing a file or directory 2 new signals: - FISSharedPathNew triggered when a new path is shared - FISSharedPathRemoved triggered when a path is not shared anymore
author Goffi <goffi@goffi.org>
date Tue, 22 May 2018 10:06:07 +0200
parents 26edcf3a30eb
children 56f94936df1e
comparison
equal deleted inserted replaced
2588:4011e4ee3151 2589:282d1314d574
95 95
96 def __contains__(self, item): 96 def __contains__(self, item):
97 return self.children.__contains__(item) 97 return self.children.__contains__(item)
98 98
99 def __iter__(self): 99 def __iter__(self):
100 return self.children.__iter__ 100 return self.children.__iter__()
101 101
102 def iteritems(self): 102 def iteritems(self):
103 return self.children.iteritems() 103 return self.children.iteritems()
104 104
105 def itervalues(self):
106 return self.children.itervalues()
107
105 def getOrCreate(self, name, type_=TYPE_VIRTUAL, access=None): 108 def getOrCreate(self, name, type_=TYPE_VIRTUAL, access=None):
106 """get a node or create a virtual one and return it""" 109 """get a node or create a virtual node and return it"""
107 if access is None: 110 if access is None:
108 access = {C.ACCESS_PERM_READ: {KEY_TYPE: C.ACCESS_TYPE_PUBLIC}} 111 access = {C.ACCESS_PERM_READ: {KEY_TYPE: C.ACCESS_TYPE_PUBLIC}}
109 try: 112 try:
110 return self.children[name] 113 return self.children[name]
111 except KeyError: 114 except KeyError:
115 def addChild(self, node): 118 def addChild(self, node):
116 if node.parent is not None: 119 if node.parent is not None:
117 raise exceptions.ConflictError(_(u"a node can't have several parents")) 120 raise exceptions.ConflictError(_(u"a node can't have several parents"))
118 node.parent = self 121 node.parent = self
119 self.children[node.name] = node 122 self.children[node.name] = node
123
124 def removeFromParent(self):
125 try:
126 del self.parent.children[self.name]
127 except TypeError:
128 raise exceptions.InternalError(u'trying to remove a node from inexisting parent')
129 except KeyError:
130 raise exceptions.InternalError(u"node not found in parent's children")
131 self.parent = None
120 132
121 def _checkNodePermission(self, client, node, perms, peer_jid): 133 def _checkNodePermission(self, client, node, perms, peer_jid):
122 """Check access to this node for peer_jid 134 """Check access to this node for peer_jid
123 135
124 @param node(SharedNode): node to check access 136 @param node(SharedNode): node to check access
190 if not node.checkPermissions(client, peer_jid, perms = perms): 202 if not node.checkPermissions(client, peer_jid, perms = perms):
191 raise exceptions.PermissionError(u"permission denied") 203 raise exceptions.PermissionError(u"permission denied")
192 204
193 return node, u'/'.join(path_elts) 205 return node, u'/'.join(path_elts)
194 206
207 def findByLocalPath(self, path):
208 """retrieve nodes linking to local path
209
210 @return (list[ShareNode]): found nodes associated to path
211 @raise exceptions.NotFound: no node has been found with this path
212 """
213 shared_paths = self.getSharedPaths()
214 try:
215 return shared_paths[path]
216 except KeyError:
217 raise exceptions.NotFound
218
219 def _getSharedPaths(self, node, paths):
220 if node.type == TYPE_VIRTUAL:
221 for node in node.itervalues():
222 self._getSharedPaths(node, paths)
223 elif node.type == TYPE_PATH:
224 paths.setdefault(node.path, []).append(node)
225 else:
226 raise exceptions.InternalError(u'unknown node type: {type}'.format(
227 type = node.type))
228
229 def getSharedPaths(self):
230 """retrieve nodes by shared path
231
232 this method will retrieve recursively shared path in children of this node
233 @return (dict): map from shared path to list of nodes
234 """
235 if self.type == TYPE_PATH:
236 raise exceptions.InternalError("getSharedPaths must be used on a virtual node")
237 paths = {}
238 self._getSharedPaths(self, paths)
239 return paths
240
195 241
196 class XEP_0329(object): 242 class XEP_0329(object):
197 243
198 def __init__(self, host): 244 def __init__(self, host):
199 log.info(_("File Information Sharing initialization")) 245 log.info(_("File Information Sharing initialization"))
200 self.host = host 246 self.host = host
201 ShareNode.host = host 247 ShareNode.host = host
202 self._h = host.plugins['XEP-0300'] 248 self._h = host.plugins['XEP-0300']
203 self._jf = host.plugins['XEP-0234'] 249 self._jf = host.plugins['XEP-0234']
204 host.bridge.addMethod("FISList", ".plugin", in_sign='ssa{ss}s', out_sign='aa{ss}', method=self._listFiles, async=True) 250 host.bridge.addMethod("FISList", ".plugin", in_sign='ssa{ss}s', out_sign='aa{ss}', method=self._listFiles, async=True)
251 host.bridge.addMethod("FISLocalSharesGet", ".plugin", in_sign='s', out_sign='as', method=self._localSharesGet)
205 host.bridge.addMethod("FISSharePath", ".plugin", in_sign='ssss', out_sign='s', method=self._sharePath) 252 host.bridge.addMethod("FISSharePath", ".plugin", in_sign='ssss', out_sign='s', method=self._sharePath)
253 host.bridge.addMethod("FISUnsharePath", ".plugin", in_sign='ss', out_sign='', method=self._unsharePath)
254 host.bridge.addSignal("FISSharedPathNew", ".plugin", signature='sss')
255 host.bridge.addSignal("FISSharedPathRemoved", ".plugin", signature='ss')
206 host.trigger.add("XEP-0234_fileSendingRequest", self._fileSendingRequestTrigger) 256 host.trigger.add("XEP-0234_fileSendingRequest", self._fileSendingRequestTrigger)
207 host.registerNamespace('fis', NS_FIS) 257 host.registerNamespace('fis', NS_FIS)
208 258
209 def getHandler(self, client): 259 def getHandler(self, client):
210 return XEP_0329_handler(self) 260 return XEP_0329_handler(self)
502 query_elt['node'] = path 552 query_elt['node'] = path
503 d = iq_elt.send() 553 d = iq_elt.send()
504 d.addCallback(self._parseResult) 554 d.addCallback(self._parseResult)
505 return d 555 return d
506 556
557 def _localSharesGet(self, profile):
558 client = self.host.getClient(profile)
559 return self.localSharesGet(client)
560
561 def localSharesGet(self, client):
562 return client._XEP_0329_root_node.getSharedPaths().keys()
563
507 def _sharePath(self, name, path, access, profile): 564 def _sharePath(self, name, path, access, profile):
508 client = self.host.getClient(profile) 565 client = self.host.getClient(profile)
509 access= json.loads(access) 566 access= json.loads(access)
510 return self.sharePath(client, name or None, path, access) 567 return self.sharePath(client, name or None, path, access)
511 568
514 raise exceptions.ClientTypeError 571 raise exceptions.ClientTypeError
515 if not os.path.exists(path): 572 if not os.path.exists(path):
516 raise ValueError(_(u"This path doesn't exist!")) 573 raise ValueError(_(u"This path doesn't exist!"))
517 if not path or not path.strip(u' /'): 574 if not path or not path.strip(u' /'):
518 raise ValueError(_(u"A path need to be specified")) 575 raise ValueError(_(u"A path need to be specified"))
576 if not isinstance(access, dict):
577 raise ValueError(_(u'access must be a dict'))
519 578
520 node = client._XEP_0329_root_node 579 node = client._XEP_0329_root_node
521 node_type = TYPE_PATH 580 node_type = TYPE_PATH
522 if os.path.isfile(path): 581 if os.path.isfile(path):
523 # we have a single file, the workflow is diferrent as we store all single files in the same dir 582 # we have a single file, the workflow is diferrent as we store all single files in the same dir
525 584
526 if not name: 585 if not name:
527 name = os.path.basename(path.rstrip(u' /')) 586 name = os.path.basename(path.rstrip(u' /'))
528 if not name: 587 if not name:
529 raise exceptions.InternalError(_(u"Can't find a proper name")) 588 raise exceptions.InternalError(_(u"Can't find a proper name"))
530
531 if not isinstance(access, dict):
532 raise ValueError(_(u'access must be a dict'))
533 589
534 if name in node or name == SINGLE_FILES_DIR: 590 if name in node or name == SINGLE_FILES_DIR:
535 idx = 1 591 idx = 1
536 new_name = name + '_' + unicode(idx) 592 new_name = name + '_' + unicode(idx)
537 while new_name in node: 593 while new_name in node:
540 name = new_name 596 name = new_name
541 log.info(_(u"A directory with this name is already shared, renamed to {new_name} [{profile}]".format( 597 log.info(_(u"A directory with this name is already shared, renamed to {new_name} [{profile}]".format(
542 new_name=new_name, profile=client.profile))) 598 new_name=new_name, profile=client.profile)))
543 599
544 ShareNode(name=name, parent=node, type_=node_type, access=access, path=path) 600 ShareNode(name=name, parent=node, type_=node_type, access=access, path=path)
601 self.host.bridge.FISSharedPathNew(path, name, client.profile)
545 return name 602 return name
603
604 def _unsharePath(self, path, profile):
605 client = self.host.getClient(profile)
606 return self.unsharePath(client, path)
607
608 def unsharePath(self, client, path):
609 nodes = client._XEP_0329_root_node.findByLocalPath(path)
610 for node in nodes:
611 node.removeFromParent()
612 self.host.bridge.FISSharedPathRemoved(path, client.profile)
546 613
547 614
548 class XEP_0329_handler(xmlstream.XMPPHandler): 615 class XEP_0329_handler(xmlstream.XMPPHandler):
549 implements(iwokkel.IDisco) 616 implements(iwokkel.IDisco)
550 617