Mercurial > libervia-backend
diff 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 |
line wrap: on
line diff
--- a/sat/plugins/plugin_xep_0329.py Wed Jun 27 07:51:29 2018 +0200 +++ b/sat/plugins/plugin_xep_0329.py Wed Jun 27 20:14:46 2018 +0200 @@ -21,6 +21,7 @@ from sat.core import exceptions from sat.core.constants import Const as C from sat.core.log import getLogger + log = getLogger(__name__) from sat.tools import stream from sat.tools.common import regex @@ -44,30 +45,35 @@ C.PI_DEPENDENCIES: ["XEP-0234", "XEP-0300"], C.PI_MAIN: "XEP_0329", C.PI_HANDLER: "yes", - C.PI_DESCRIPTION: _(u"""Implementation of File Information Sharing""") + C.PI_DESCRIPTION: _(u"""Implementation of File Information Sharing"""), } -NS_FIS = 'urn:xmpp:fis:0' +NS_FIS = "urn:xmpp:fis:0" IQ_FIS_REQUEST = C.IQ_GET + '/query[@xmlns="' + NS_FIS + '"]' SINGLE_FILES_DIR = u"files" -TYPE_VIRTUAL= u'virtual' -TYPE_PATH = u'path' -SHARE_TYPES = (TYPE_PATH, TYPE_VIRTUAL) -KEY_TYPE = u'type' +TYPE_VIRTUAL = u"virtual" +TYPE_PATH = u"path" +SHARE_TYPES = (TYPE_PATH, TYPE_VIRTUAL) +KEY_TYPE = u"type" class ShareNode(object): """node containing directory or files to share, virtual or real""" + host = None def __init__(self, name, parent, type_, access, path=None): assert type_ in SHARE_TYPES if name is not None: - if name == u'..' or u'/' in name or u'\\' in name: - log.warning(_(u'path change chars found in name [{name}], hack attempt?').format(name=name)) - if name == u'..': - name = u'--' + if name == u".." or u"/" in name or u"\\" in name: + log.warning( + _(u"path change chars found in name [{name}], hack attempt?").format( + name=name + ) + ) + if name == u"..": + name = u"--" else: name = regex.pathEscape(name) self.name = name @@ -125,7 +131,9 @@ try: del self.parent.children[self.name] except TypeError: - raise exceptions.InternalError(u'trying to remove a node from inexisting parent') + raise exceptions.InternalError( + u"trying to remove a node from inexisting parent" + ) except KeyError: raise exceptions.InternalError(u"node not found in parent's children") self.parent = None @@ -138,7 +146,7 @@ @param peer_jid(jid.JID): entity which try to access the node @return (bool): True if entity can access """ - file_data = {u'access':self.access, u'owner': client.jid.userhostJID()} + file_data = {u"access": self.access, u"owner": client.jid.userhostJID()} try: self.host.memory.checkFilePermission(file_data, peer_jid, perms) except exceptions.PermissionError: @@ -146,7 +154,9 @@ else: return True - def checkPermissions(self, client, peer_jid, perms=(C.ACCESS_PERM_READ,), check_parents=True): + def checkPermissions( + self, client, peer_jid, perms=(C.ACCESS_PERM_READ,), check_parents=True + ): """check that peer_jid can access this node and all its parents @param peer_jid(jid.JID): entrity trying to access the node @@ -178,15 +188,18 @@ @raise exceptions.DataError: path is invalid @raise NotFound: path lead to a non existing file/directory """ - path_elts = filter(None, path.split(u'/')) + path_elts = filter(None, path.split(u"/")) - if u'..' in path_elts: - log.warning(_(u'parent dir ("..") found in path, hack attempt? path is {path} [{profile}]').format( - path=path, profile=client.profile)) + if u".." in path_elts: + log.warning( + _( + u'parent dir ("..") found in path, hack attempt? path is {path} [{profile}]' + ).format(path=path, profile=client.profile) + ) raise exceptions.PermissionError(u"illegal path elements") if not path_elts: - raise exceptions.DataError(_(u'path is invalid: {path}').format(path=path)) + raise exceptions.DataError(_(u"path is invalid: {path}").format(path=path)) node = client._XEP_0329_root_node @@ -199,10 +212,10 @@ elif node.type == TYPE_PATH: break - if not node.checkPermissions(client, peer_jid, perms = perms): + if not node.checkPermissions(client, peer_jid, perms=perms): raise exceptions.PermissionError(u"permission denied") - return node, u'/'.join(path_elts) + return node, u"/".join(path_elts) def findByLocalPath(self, path): """retrieve nodes linking to local path @@ -223,8 +236,9 @@ elif node.type == TYPE_PATH: paths.setdefault(node.path, []).append(node) else: - raise exceptions.InternalError(u'unknown node type: {type}'.format( - type = node.type)) + raise exceptions.InternalError( + u"unknown node type: {type}".format(type=node.type) + ) def getSharedPaths(self): """retrieve nodes by shared path @@ -233,38 +247,71 @@ @return (dict): map from shared path to list of nodes """ if self.type == TYPE_PATH: - raise exceptions.InternalError("getSharedPaths must be used on a virtual node") + raise exceptions.InternalError( + "getSharedPaths must be used on a virtual node" + ) paths = {} self._getSharedPaths(self, paths) return paths class XEP_0329(object): - def __init__(self, host): log.info(_("File Information Sharing initialization")) self.host = host ShareNode.host = host - self._h = host.plugins['XEP-0300'] - self._jf = host.plugins['XEP-0234'] - host.bridge.addMethod("FISList", ".plugin", in_sign='ssa{ss}s', out_sign='aa{ss}', method=self._listFiles, async=True) - host.bridge.addMethod("FISLocalSharesGet", ".plugin", in_sign='s', out_sign='as', method=self._localSharesGet) - host.bridge.addMethod("FISSharePath", ".plugin", in_sign='ssss', out_sign='s', method=self._sharePath) - host.bridge.addMethod("FISUnsharePath", ".plugin", in_sign='ss', out_sign='', method=self._unsharePath) - host.bridge.addSignal("FISSharedPathNew", ".plugin", signature='sss') - host.bridge.addSignal("FISSharedPathRemoved", ".plugin", signature='ss') + self._h = host.plugins["XEP-0300"] + self._jf = host.plugins["XEP-0234"] + host.bridge.addMethod( + "FISList", + ".plugin", + in_sign="ssa{ss}s", + out_sign="aa{ss}", + method=self._listFiles, + async=True, + ) + host.bridge.addMethod( + "FISLocalSharesGet", + ".plugin", + in_sign="s", + out_sign="as", + method=self._localSharesGet, + ) + host.bridge.addMethod( + "FISSharePath", + ".plugin", + in_sign="ssss", + out_sign="s", + method=self._sharePath, + ) + host.bridge.addMethod( + "FISUnsharePath", + ".plugin", + in_sign="ss", + out_sign="", + method=self._unsharePath, + ) + host.bridge.addSignal("FISSharedPathNew", ".plugin", signature="sss") + host.bridge.addSignal("FISSharedPathRemoved", ".plugin", signature="ss") host.trigger.add("XEP-0234_fileSendingRequest", self._fileSendingRequestTrigger) - host.registerNamespace('fis', NS_FIS) + host.registerNamespace("fis", NS_FIS) def getHandler(self, client): return XEP_0329_handler(self) def profileConnected(self, client): if not client.is_component: - client._XEP_0329_root_node = ShareNode(None, None, TYPE_VIRTUAL, {C.ACCESS_PERM_READ: {KEY_TYPE: C.ACCESS_TYPE_PUBLIC}}) - client._XEP_0329_names_data = {} # name to share map + client._XEP_0329_root_node = ShareNode( + None, + None, + TYPE_VIRTUAL, + {C.ACCESS_PERM_READ: {KEY_TYPE: C.ACCESS_TYPE_PUBLIC}}, + ) + client._XEP_0329_names_data = {} # name to share map - def _fileSendingRequestTrigger(self, client, session, content_data, content_name, file_data, file_elt): + def _fileSendingRequestTrigger( + self, client, session, content_data, content_name, file_data, file_elt + ): """this trigger check that a requested file is available, and fill suitable data if so path and name are used to retrieve the file. If path is missing, we try our luck with known names @@ -273,21 +320,21 @@ return True, None try: - name = file_data[u'name'] + name = file_data[u"name"] except KeyError: return True, None - assert u'/' not in name + assert u"/" not in name - path = file_data.get(u'path') + path = file_data.get(u"path") if path is not None: # we have a path, we can follow it to find node try: - node, rem_path = ShareNode.find(client, path, session[u'peer_jid']) + node, rem_path = ShareNode.find(client, path, session[u"peer_jid"]) except (exceptions.PermissionError, exceptions.NotFound): - # no file, or file not allowed, we continue normal workflow + # no file, or file not allowed, we continue normal workflow return True, None except exceptions.DataError: - log.warning(_(u'invalid path: {path}').format(path=path)) + log.warning(_(u"invalid path: {path}").format(path=path)) return True, None if node.type == TYPE_VIRTUAL: @@ -300,7 +347,9 @@ # we have a path node, so we can retrieve the full path now path = os.path.join(node.path, rem_path, name) else: - raise exceptions.InternalError(u'unknown type: {type}'.format(type=node.type)) + raise exceptions.InternalError( + u"unknown type: {type}".format(type=node.type) + ) if not os.path.exists(path): return True, None size = os.path.getsize(path) @@ -312,49 +361,56 @@ return True, None for path, shared_file in name_data.iteritems(): - if True: # FIXME: filters are here + if True: # FIXME: filters are here break else: return True, None - parent_node = shared_file[u'parent'] - if not parent_node.checkPermissions(client, session[u'peer_jid']): - log.warning(_(u"{peer_jid} requested a file (s)he can't access [{profile}]").format( - peer_jid = session[u'peer_jid'], profile = client.profile)) + parent_node = shared_file[u"parent"] + if not parent_node.checkPermissions(client, session[u"peer_jid"]): + log.warning( + _( + u"{peer_jid} requested a file (s)he can't access [{profile}]" + ).format(peer_jid=session[u"peer_jid"], profile=client.profile) + ) return True, None - size = shared_file[u'size'] + size = shared_file[u"size"] - file_data[u'size'] = size - file_elt.addElement(u'size', content=unicode(size)) - hash_algo = file_data[u'hash_algo'] = self._h.getDefaultAlgo() - hasher = file_data[u'hash_hasher'] = self._h.getHasher(hash_algo) + file_data[u"size"] = size + file_elt.addElement(u"size", content=unicode(size)) + hash_algo = file_data[u"hash_algo"] = self._h.getDefaultAlgo() + hasher = file_data[u"hash_hasher"] = self._h.getHasher(hash_algo) file_elt.addChild(self._h.buildHashUsedElt(hash_algo)) - content_data['stream_object'] = stream.FileStreamObject( + content_data["stream_object"] = stream.FileStreamObject( self.host, client, path, uid=self._jf.getProgressId(session, content_name), size=size, data_cb=lambda data: hasher.update(data), - ) + ) return False, True # common methods def _requestHandler(self, client, iq_elt, root_nodes_cb, files_from_node_cb): iq_elt.handled = True - owner = jid.JID(iq_elt['from']).userhostJID() - node = iq_elt.query.getAttribute('node') + owner = jid.JID(iq_elt["from"]).userhostJID() + node = iq_elt.query.getAttribute("node") if not node: d = defer.maybeDeferred(root_nodes_cb, client, iq_elt, owner) else: d = defer.maybeDeferred(files_from_node_cb, client, iq_elt, owner, node) - d.addErrback(lambda failure_: log.error(_(u"error while retrieving files: {msg}").format(msg=failure_))) + d.addErrback( + lambda failure_: log.error( + _(u"error while retrieving files: {msg}").format(msg=failure_) + ) + ) def _iqError(self, client, iq_elt, condition="item-not-found"): error_elt = jabber_error.StanzaError(condition).toResponse(iq_elt) client.send(error_elt) - # client + # client def _addPathData(self, client, query_elt, path, parent_node): """Fill query_elt with files/directories found in path""" @@ -362,23 +418,24 @@ if os.path.isfile(path): size = os.path.getsize(path) mime_type = mimetypes.guess_type(path, strict=False)[0] - file_elt = self._jf.buildFileElement(name = name, - size = size, - mime_type = mime_type, - modified = os.path.getmtime(path)) + file_elt = self._jf.buildFileElement( + name=name, size=size, mime_type=mime_type, modified=os.path.getmtime(path) + ) query_elt.addChild(file_elt) # we don't specify hash as it would be too resource intensive to calculate it for all files # we add file to name_data, so users can request it later name_data = client._XEP_0329_names_data.setdefault(name, {}) if path not in name_data: - name_data[path] = {'size': size, - 'mime_type': mime_type, - 'parent': parent_node} + name_data[path] = { + "size": size, + "mime_type": mime_type, + "parent": parent_node, + } else: # we have a directory - directory_elt = query_elt.addElement('directory') - directory_elt['name'] = name + directory_elt = query_elt.addElement("directory") + directory_elt["name"] = name def _pathNodeHandler(self, client, iq_elt, query_elt, node, path): """Fill query_elt for path nodes, i.e. physical directories""" @@ -390,13 +447,15 @@ elif os.path.isfile(path): self._addPathData(client, query_elt, path, node) else: - for name in sorted(os.listdir(path.encode('utf-8')), key=lambda n: n.lower()): + for name in sorted(os.listdir(path.encode("utf-8")), key=lambda n: n.lower()): try: - name = name.decode('utf-8', 'strict') + name = name.decode("utf-8", "strict") except UnicodeDecodeError as e: - log.warning(_(u"ignoring invalid unicode name ({name}): {msg}").format( - name = name.decode('utf-8', 'replace'), - msg = e)) + log.warning( + _(u"ignoring invalid unicode name ({name}): {msg}").format( + name=name.decode("utf-8", "replace"), msg=e + ) + ) continue full_path = os.path.join(path, name) self._addPathData(client, query_elt, full_path, node) @@ -408,68 +467,79 @@ continue node_type = child_node.type if node_type == TYPE_VIRTUAL: - directory_elt = query_elt.addElement('directory') - directory_elt['name'] = name + directory_elt = query_elt.addElement("directory") + directory_elt["name"] = name elif node_type == TYPE_PATH: self._addPathData(client, query_elt, child_node.path, child_node) else: - raise exceptions.InternalError(_(u'unexpected type: {type}').format(type=node_type)) + raise exceptions.InternalError( + _(u"unexpected type: {type}").format(type=node_type) + ) def _getRootNodesCb(self, client, iq_elt, owner): - peer_jid = jid.JID(iq_elt['from']) - iq_result_elt = xmlstream.toResponse(iq_elt, 'result') - query_elt = iq_result_elt.addElement((NS_FIS, 'query')) + peer_jid = jid.JID(iq_elt["from"]) + iq_result_elt = xmlstream.toResponse(iq_elt, "result") + query_elt = iq_result_elt.addElement((NS_FIS, "query")) for name, node in client._XEP_0329_root_node.iteritems(): if not node.checkPermissions(client, peer_jid, check_parents=False): continue - directory_elt = query_elt.addElement('directory') - directory_elt['name'] = name + directory_elt = query_elt.addElement("directory") + directory_elt["name"] = name client.send(iq_result_elt) def _getFilesFromNodeCb(self, client, iq_elt, owner, node_path): """Main method to retrieve files/directories from a node_path""" - peer_jid = jid.JID(iq_elt[u'from']) + peer_jid = jid.JID(iq_elt[u"from"]) try: node, path = ShareNode.find(client, node_path, peer_jid) except (exceptions.PermissionError, exceptions.NotFound): return self._iqError(client, iq_elt) except exceptions.DataError: - return self._iqError(client, iq_elt, condition='not-acceptable') + return self._iqError(client, iq_elt, condition="not-acceptable") node_type = node.type - peer_jid = jid.JID(iq_elt['from']) - iq_result_elt = xmlstream.toResponse(iq_elt, 'result') - query_elt = iq_result_elt.addElement((NS_FIS, 'query')) - query_elt[u'node'] = node_path + peer_jid = jid.JID(iq_elt["from"]) + iq_result_elt = xmlstream.toResponse(iq_elt, "result") + query_elt = iq_result_elt.addElement((NS_FIS, "query")) + query_elt[u"node"] = node_path # we now fill query_elt according to node_type if node_type == TYPE_PATH: - # it's a physical path + # it's a physical path self._pathNodeHandler(client, iq_elt, query_elt, node, path) elif node_type == TYPE_VIRTUAL: assert not path self._virtualNodeHandler(client, peer_jid, iq_elt, query_elt, node) else: - raise exceptions.InternalError(_(u'unknown node type: {type}').format(type=node_type)) + raise exceptions.InternalError( + _(u"unknown node type: {type}").format(type=node_type) + ) client.send(iq_result_elt) def onRequest(self, iq_elt, client): - return self._requestHandler(client, iq_elt, self._getRootNodesCb, self._getFilesFromNodeCb) + return self._requestHandler( + client, iq_elt, self._getRootNodesCb, self._getFilesFromNodeCb + ) # Component @defer.inlineCallbacks def _compGetRootNodesCb(self, client, iq_elt, owner): - peer_jid = jid.JID(iq_elt['from']) - files_data = yield self.host.memory.getFiles(client, peer_jid=peer_jid, parent=u'', - type_=C.FILE_TYPE_DIRECTORY, owner=owner) - iq_result_elt = xmlstream.toResponse(iq_elt, 'result') - query_elt = iq_result_elt.addElement((NS_FIS, 'query')) + peer_jid = jid.JID(iq_elt["from"]) + files_data = yield self.host.memory.getFiles( + client, + peer_jid=peer_jid, + parent=u"", + type_=C.FILE_TYPE_DIRECTORY, + owner=owner, + ) + iq_result_elt = xmlstream.toResponse(iq_elt, "result") + query_elt = iq_result_elt.addElement((NS_FIS, "query")) for file_data in files_data: - name = file_data[u'name'] - directory_elt = query_elt.addElement(u'directory') - directory_elt[u'name'] = name + name = file_data[u"name"] + directory_elt = query_elt.addElement(u"directory") + directory_elt[u"name"] = name client.send(iq_result_elt) @defer.inlineCallbacks @@ -479,44 +549,53 @@ result stanza is then built and sent to requestor @trigger XEP-0329_compGetFilesFromNode(client, iq_elt, owner, node_path, files_data): can be used to add data/elements """ - peer_jid = jid.JID(iq_elt['from']) + peer_jid = jid.JID(iq_elt["from"]) try: - files_data = yield self.host.memory.getFiles(client, peer_jid=peer_jid, path=node_path, owner=owner) + files_data = yield self.host.memory.getFiles( + client, peer_jid=peer_jid, path=node_path, owner=owner + ) except exceptions.NotFound: self._iqError(client, iq_elt) return - iq_result_elt = xmlstream.toResponse(iq_elt, 'result') - query_elt = iq_result_elt.addElement((NS_FIS, 'query')) - query_elt[u'node'] = node_path - if not self.host.trigger.point(u'XEP-0329_compGetFilesFromNode', client, iq_elt, owner, node_path, files_data): + iq_result_elt = xmlstream.toResponse(iq_elt, "result") + query_elt = iq_result_elt.addElement((NS_FIS, "query")) + query_elt[u"node"] = node_path + if not self.host.trigger.point( + u"XEP-0329_compGetFilesFromNode", client, iq_elt, owner, node_path, files_data + ): return for file_data in files_data: - file_elt = self._jf.buildFileElementFromDict(file_data, - modified=file_data.get(u'modified', file_data[u'created'])) + file_elt = self._jf.buildFileElementFromDict( + file_data, modified=file_data.get(u"modified", file_data[u"created"]) + ) query_elt.addChild(file_elt) client.send(iq_result_elt) def onComponentRequest(self, iq_elt, client): - return self._requestHandler(client, iq_elt, self._compGetRootNodesCb, self._compGetFilesFromNodeCb) + return self._requestHandler( + client, iq_elt, self._compGetRootNodesCb, self._compGetFilesFromNodeCb + ) def _parseResult(self, iq_elt): - query_elt = next(iq_elt.elements(NS_FIS, 'query')) + query_elt = next(iq_elt.elements(NS_FIS, "query")) files = [] for elt in query_elt.elements(): - if elt.name == 'file': + if elt.name == "file": # we have a file try: file_data = self._jf.parseFileElement(elt) except exceptions.DataError: continue - file_data[u'type'] = C.FILE_TYPE_FILE - elif elt.name == 'directory' and elt.uri == NS_FIS: + file_data[u"type"] = C.FILE_TYPE_FILE + elif elt.name == "directory" and elt.uri == NS_FIS: # we have a directory - file_data = {'name': elt['name'], 'type': C.FILE_TYPE_DIRECTORY} + file_data = {"name": elt["name"], "type": C.FILE_TYPE_DIRECTORY} else: - log.warning(_(u"unexpected element, ignoring: {elt}").format(elt=elt.toXml())) + log.warning( + _(u"unexpected element, ignoring: {elt}").format(elt=elt.toXml()) + ) continue files.append(file_data) return files @@ -526,7 +605,9 @@ def _serializeData(self, files_data): for file_data in files_data: for key, value in file_data.iteritems(): - file_data[key] = json.dumps(value) if key in ('extra',) else unicode(value) + file_data[key] = ( + json.dumps(value) if key in ("extra",) else unicode(value) + ) return files_data def _listFiles(self, target_jid, path, extra, profile): @@ -545,11 +626,11 @@ @param extra(dict, None): extra data @return list(dict): shared files """ - iq_elt = client.IQ('get') - iq_elt['to'] = target_jid.full() - query_elt = iq_elt.addElement((NS_FIS, 'query')) + iq_elt = client.IQ("get") + iq_elt["to"] = target_jid.full() + query_elt = iq_elt.addElement((NS_FIS, "query")) if path: - query_elt['node'] = path + query_elt["node"] = path d = iq_elt.send() d.addCallback(self._parseResult) return d @@ -563,7 +644,7 @@ def _sharePath(self, name, path, access, profile): client = self.host.getClient(profile) - access= json.loads(access) + access = json.loads(access) return self.sharePath(client, name or None, path, access) def sharePath(self, client, name, path, access): @@ -571,31 +652,36 @@ raise exceptions.ClientTypeError if not os.path.exists(path): raise ValueError(_(u"This path doesn't exist!")) - if not path or not path.strip(u' /'): + if not path or not path.strip(u" /"): raise ValueError(_(u"A path need to be specified")) if not isinstance(access, dict): - raise ValueError(_(u'access must be a dict')) + raise ValueError(_(u"access must be a dict")) node = client._XEP_0329_root_node node_type = TYPE_PATH if os.path.isfile(path): - # we have a single file, the workflow is diferrent as we store all single files in the same dir + # we have a single file, the workflow is diferrent as we store all single files in the same dir node = node.getOrCreate(SINGLE_FILES_DIR) if not name: - name = os.path.basename(path.rstrip(u' /')) + name = os.path.basename(path.rstrip(u" /")) if not name: raise exceptions.InternalError(_(u"Can't find a proper name")) if name in node or name == SINGLE_FILES_DIR: idx = 1 - new_name = name + '_' + unicode(idx) + new_name = name + "_" + unicode(idx) while new_name in node: idx += 1 - new_name = name + '_' + unicode(idx) + new_name = name + "_" + unicode(idx) name = new_name - log.info(_(u"A directory with this name is already shared, renamed to {new_name} [{profile}]".format( - new_name=new_name, profile=client.profile))) + log.info( + _( + u"A directory with this name is already shared, renamed to {new_name} [{profile}]".format( + new_name=new_name, profile=client.profile + ) + ) + ) ShareNode(name=name, parent=node, type_=node_type, access=access, path=path) self.host.bridge.FISSharedPathNew(path, name, client.profile) @@ -621,12 +707,16 @@ def connectionInitialized(self): if self.parent.is_component: - self.xmlstream.addObserver(IQ_FIS_REQUEST, self.plugin_parent.onComponentRequest, client=self.parent) + self.xmlstream.addObserver( + IQ_FIS_REQUEST, self.plugin_parent.onComponentRequest, client=self.parent + ) else: - self.xmlstream.addObserver(IQ_FIS_REQUEST, self.plugin_parent.onRequest, client=self.parent) + self.xmlstream.addObserver( + IQ_FIS_REQUEST, self.plugin_parent.onRequest, client=self.parent + ) - def getDiscoInfo(self, requestor, target, nodeIdentifier=''): + def getDiscoInfo(self, requestor, target, nodeIdentifier=""): return [disco.DiscoFeature(NS_FIS)] - def getDiscoItems(self, requestor, target, nodeIdentifier=''): + def getDiscoItems(self, requestor, target, nodeIdentifier=""): return []