Mercurial > libervia-backend
diff libervia/backend/plugins/plugin_xep_0329.py @ 4270:0d7bb4df2343
Reformatted code base using black.
author | Goffi <goffi@goffi.org> |
---|---|
date | Wed, 19 Jun 2024 18:44:57 +0200 |
parents | 4b842c1fb686 |
children |
line wrap: on
line diff
--- a/libervia/backend/plugins/plugin_xep_0329.py Tue Jun 18 12:06:45 2024 +0200 +++ b/libervia/backend/plugins/plugin_xep_0329.py Wed Jun 19 18:44:57 2024 +0200 @@ -209,9 +209,12 @@ path_elts = [_f for _f in path.split("/") if _f] if ".." in path_elts: - log.warning(_( - 'parent dir ("..") found in path, hack attempt? path is {path} ' - '[{profile}]').format(path=path, profile=client.profile)) + log.warning( + _( + 'parent dir ("..") found in path, hack attempt? path is {path} ' + "[{profile}]" + ).format(path=path, profile=client.profile) + ) raise exceptions.PermissionError("illegal path elements") node = client._XEP_0329_root_node @@ -347,7 +350,9 @@ ) host.bridge.add_signal("fis_shared_path_new", ".plugin", signature="sss") host.bridge.add_signal("fis_shared_path_removed", ".plugin", signature="ss") - host.trigger.add("XEP-0234_fileSendingRequest", self._file_sending_request_trigger) + host.trigger.add( + "XEP-0234_fileSendingRequest", self._file_sending_request_trigger + ) host.register_namespace("fis", NS_FIS) def get_handler(self, client): @@ -356,8 +361,8 @@ def profile_connected(self, client): if client.is_component: client._file_sharing_allowed_hosts = self.host.memory.config_get( - 'component file_sharing', - 'http_upload_allowed_hosts_list') or [client.host] + "component file_sharing", "http_upload_allowed_hosts_list" + ) or [client.host] else: client._XEP_0329_root_node = ShareNode( None, @@ -477,8 +482,11 @@ size = os.path.getsize(path) mime_type = mimetypes.guess_type(path, strict=False)[0] file_elt = self._jf.build_file_element( - client=client, name=name, size=size, mime_type=mime_type, - modified=os.path.getmtime(path) + client=client, + name=name, + size=size, + mime_type=mime_type, + modified=os.path.getmtime(path), ) query_elt.addChild(file_elt) @@ -625,12 +633,12 @@ self._iq_error(client, iq_elt) return except exceptions.PermissionError: - self._iq_error(client, iq_elt, condition='not-allowed') + self._iq_error(client, iq_elt, condition="not-allowed") return except Exception as e: tb = traceback.format_tb(e.__traceback__) log.error(f"internal server error: {e}\n{''.join(tb)}") - self._iq_error(client, iq_elt, condition='internal-server-error') + self._iq_error(client, iq_elt, condition="internal-server-error") return iq_result_elt = xmlstream.toResponse(iq_elt, "result") query_elt = iq_result_elt.addElement((NS_FIS, "query")) @@ -642,13 +650,13 @@ iq_result_elt, owner, node_path, - files_data + files_data, ): return for file_data in files_data: - if file_data['type'] == C.FILE_TYPE_DIRECTORY: + if file_data["type"] == C.FILE_TYPE_DIRECTORY: directory_elt = query_elt.addElement("directory") - directory_elt['name'] = file_data['name'] + directory_elt["name"] = file_data["name"] self.host.trigger.point( "XEP-0329_compGetFilesFromNode_build_directory", client, @@ -661,14 +669,17 @@ file_elt = self._jf.build_file_element_from_dict( client, file_data, - modified=file_data.get("modified", file_data["created"]) + modified=file_data.get("modified", file_data["created"]), ) query_elt.addChild(file_elt) client.send(iq_result_elt) def on_component_request(self, iq_elt, client): return self._request_handler( - client, iq_elt, self._comp_get_root_nodes_cb, self._comp_get_files_from_node_cb + client, + iq_elt, + self._comp_get_root_nodes_cb, + self._comp_get_files_from_node_cb, ) async def _parse_result(self, client, peer_jid, iq_elt): @@ -684,18 +695,22 @@ continue file_data["type"] = C.FILE_TYPE_FILE try: - thumbs = file_data['extra'][C.KEY_THUMBNAILS] + thumbs = file_data["extra"][C.KEY_THUMBNAILS] except KeyError: log.debug(f"No thumbnail found for {file_data}") else: for thumb in thumbs: - if 'url' not in thumb and "id" in thumb: + if "url" not in thumb and "id" in thumb: try: - file_path = await self._b.get_file(client, peer_jid, thumb['id']) + file_path = await self._b.get_file( + client, peer_jid, thumb["id"] + ) except Exception as e: - log.warning(f"Can't get thumbnail {thumb['id']!r} for {file_data}: {e}") + log.warning( + f"Can't get thumbnail {thumb['id']!r} for {file_data}: {e}" + ) else: - thumb['filename'] = file_path.name + thumb["filename"] = file_path.name elif elt.name == "directory" and elt.uri == NS_FIS: # we have a directory @@ -709,8 +724,7 @@ ) else: log.warning( - _("unexpected element, ignoring: {elt}") - .format(elt=elt.toXml()) + _("unexpected element, ignoring: {elt}").format(elt=elt.toXml()) ) continue files.append(file_data) @@ -721,10 +735,10 @@ async def _parse_element(self, client, iq_elt, element, namespace): peer_jid, owner = client.get_owner_and_peer(iq_elt) elt = next(iq_elt.elements(namespace, element)) - path = Path("/", elt['path']) + path = Path("/", elt["path"]) if len(path.parts) < 2: raise RootPathException - namespace = elt.getAttribute('namespace') + namespace = elt.getAttribute("namespace") files_data = await self.host.memory.get_files( client, peer_jid=peer_jid, @@ -734,7 +748,7 @@ owner=owner, ) if len(files_data) != 1: - client.sendError(iq_elt, 'item-not-found') + client.sendError(iq_elt, "item-not-found") raise exceptions.CancelError file_data = files_data[0] return peer_jid, elt, path, namespace, file_data @@ -742,8 +756,9 @@ def _affiliations_get(self, service_jid_s, namespace, path, profile): client = self.host.get_client(profile) service = jid.JID(service_jid_s) - d = defer.ensureDeferred(self.affiliationsGet( - client, service, namespace or None, path)) + d = defer.ensureDeferred( + self.affiliationsGet(client, service, namespace or None, path) + ) d.addCallback( lambda affiliations: { str(entity): affiliation for entity, affiliation in affiliations.items() @@ -752,33 +767,38 @@ return d async def affiliationsGet( - self, - client: SatXMPPEntity, - service: jid.JID, - namespace: Optional[str], - path: str + self, client: SatXMPPEntity, service: jid.JID, namespace: Optional[str], path: str ) -> Dict[jid.JID, str]: if not path: raise ValueError(f"invalid path: {path!r}") iq_elt = client.IQ("get") - iq_elt['to'] = service.full() + iq_elt["to"] = service.full() affiliations_elt = iq_elt.addElement((NS_FIS_AFFILIATION, "affiliations")) if namespace: affiliations_elt["namespace"] = namespace affiliations_elt["path"] = path iq_result_elt = await iq_elt.send() try: - affiliations_elt = next(iq_result_elt.elements(NS_FIS_AFFILIATION, "affiliations")) + affiliations_elt = next( + iq_result_elt.elements(NS_FIS_AFFILIATION, "affiliations") + ) except StopIteration: - raise exceptions.DataError(f"Invalid result to affiliations request: {iq_result_elt.toXml()}") + raise exceptions.DataError( + f"Invalid result to affiliations request: {iq_result_elt.toXml()}" + ) affiliations = {} - for affiliation_elt in affiliations_elt.elements(NS_FIS_AFFILIATION, 'affiliation'): + for affiliation_elt in affiliations_elt.elements( + NS_FIS_AFFILIATION, "affiliation" + ): try: - affiliations[jid.JID(affiliation_elt['jid'])] = affiliation_elt['affiliation'] + affiliations[jid.JID(affiliation_elt["jid"])] = affiliation_elt[ + "affiliation" + ] except (KeyError, RuntimeError): raise exceptions.DataError( - f"invalid affiliation element: {affiliation_elt.toXml()}") + f"invalid affiliation element: {affiliation_elt.toXml()}" + ) return affiliations @@ -786,8 +806,9 @@ client = self.host.get_client(profile) service = jid.JID(service_jid_s) affiliations = {jid.JID(e): a for e, a in affiliations.items()} - return defer.ensureDeferred(self.affiliationsSet( - client, service, namespace or None, path, affiliations)) + return defer.ensureDeferred( + self.affiliationsSet(client, service, namespace or None, path, affiliations) + ) async def affiliationsSet( self, @@ -800,15 +821,15 @@ if not path: raise ValueError(f"invalid path: {path!r}") iq_elt = client.IQ("set") - iq_elt['to'] = service.full() + iq_elt["to"] = service.full() affiliations_elt = iq_elt.addElement((NS_FIS_AFFILIATION, "affiliations")) if namespace: affiliations_elt["namespace"] = namespace affiliations_elt["path"] = path for entity_jid, affiliation in affiliations.items(): - affiliation_elt = affiliations_elt.addElement('affiliation') - affiliation_elt['jid'] = entity_jid.full() - affiliation_elt['affiliation'] = affiliation + affiliation_elt = affiliations_elt.addElement("affiliation") + affiliation_elt["jid"] = entity_jid.full() + affiliation_elt["affiliation"] = affiliation await iq_elt.send() def _on_component_affiliations_get(self, iq_elt, client): @@ -817,16 +838,18 @@ async def on_component_affiliations_get(self, client, iq_elt): try: - ( - from_jid, affiliations_elt, path, namespace, file_data - ) = await self._parse_element(client, iq_elt, "affiliations", NS_FIS_AFFILIATION) + (from_jid, affiliations_elt, path, namespace, file_data) = ( + await self._parse_element( + client, iq_elt, "affiliations", NS_FIS_AFFILIATION + ) + ) except exceptions.CancelError: return except RootPathException: # if root path is requested, we only get owner affiliation peer_jid, owner = client.get_owner_and_peer(iq_elt) is_owner = peer_jid.userhostJID() == owner - affiliations = {owner: 'owner'} + affiliations = {owner: "owner"} except exceptions.NotFound: client.sendError(iq_elt, "item-not-found") return @@ -835,17 +858,17 @@ return else: from_jid_bare = from_jid.userhostJID() - is_owner = from_jid_bare == file_data.get('owner') + is_owner = from_jid_bare == file_data.get("owner") affiliations = self.host.memory.get_file_affiliations(file_data) iq_result_elt = xmlstream.toResponse(iq_elt, "result") - affiliations_elt = iq_result_elt.addElement((NS_FIS_AFFILIATION, 'affiliations')) + affiliations_elt = iq_result_elt.addElement((NS_FIS_AFFILIATION, "affiliations")) for entity_jid, affiliation in affiliations.items(): if not is_owner and entity_jid.userhostJID() != from_jid_bare: # only onwer can get all affiliations continue - affiliation_elt = affiliations_elt.addElement('affiliation') - affiliation_elt['jid'] = entity_jid.userhost() - affiliation_elt['affiliation'] = affiliation + affiliation_elt = affiliations_elt.addElement("affiliation") + affiliation_elt["jid"] = entity_jid.userhost() + affiliation_elt["affiliation"] = affiliation client.send(iq_result_elt) def _on_component_affiliations_set(self, iq_elt, client): @@ -854,41 +877,41 @@ async def on_component_affiliations_set(self, client, iq_elt): try: - ( - from_jid, affiliations_elt, path, namespace, file_data - ) = await self._parse_element(client, iq_elt, "affiliations", NS_FIS_AFFILIATION) + (from_jid, affiliations_elt, path, namespace, file_data) = ( + await self._parse_element( + client, iq_elt, "affiliations", NS_FIS_AFFILIATION + ) + ) except exceptions.CancelError: return except RootPathException: - client.sendError(iq_elt, 'bad-request', "Root path can't be used") + client.sendError(iq_elt, "bad-request", "Root path can't be used") return - if from_jid.userhostJID() != file_data['owner']: + if from_jid.userhostJID() != file_data["owner"]: log.warning( f"{from_jid} tried to modify {path} affiliations while the owner is " f"{file_data['owner']}" ) - client.sendError(iq_elt, 'forbidden') + client.sendError(iq_elt, "forbidden") return try: affiliations = { - jid.JID(e['jid']): e['affiliation'] - for e in affiliations_elt.elements(NS_FIS_AFFILIATION, 'affiliation') + jid.JID(e["jid"]): e["affiliation"] + for e in affiliations_elt.elements(NS_FIS_AFFILIATION, "affiliation") } except (KeyError, RuntimeError): - log.warning( - f"invalid affiliation element: {affiliations_elt.toXml()}" - ) - client.sendError(iq_elt, 'bad-request', "invalid affiliation element") - return + log.warning(f"invalid affiliation element: {affiliations_elt.toXml()}") + client.sendError(iq_elt, "bad-request", "invalid affiliation element") + return except Exception as e: - log.error( - f"unexepected exception while setting affiliation element: {e}\n" - f"{affiliations_elt.toXml()}" - ) - client.sendError(iq_elt, 'internal-server-error', f"{e}") - return + log.error( + f"unexepected exception while setting affiliation element: {e}\n" + f"{affiliations_elt.toXml()}" + ) + client.sendError(iq_elt, "internal-server-error", f"{e}") + return await self.host.memory.set_file_affiliations(client, file_data, affiliations) @@ -900,8 +923,9 @@ def _configuration_get(self, service_jid_s, namespace, path, profile): client = self.host.get_client(profile) service = jid.JID(service_jid_s) - d = defer.ensureDeferred(self.configuration_get( - client, service, namespace or None, path)) + d = defer.ensureDeferred( + self.configuration_get(client, service, namespace or None, path) + ) d.addCallback( lambda configuration: { str(entity): affiliation for entity, affiliation in configuration.items() @@ -910,25 +934,25 @@ return d async def configuration_get( - self, - client: SatXMPPEntity, - service: jid.JID, - namespace: Optional[str], - path: str + self, client: SatXMPPEntity, service: jid.JID, namespace: Optional[str], path: str ) -> Dict[str, str]: if not path: raise ValueError(f"invalid path: {path!r}") iq_elt = client.IQ("get") - iq_elt['to'] = service.full() + iq_elt["to"] = service.full() configuration_elt = iq_elt.addElement((NS_FIS_CONFIGURATION, "configuration")) if namespace: configuration_elt["namespace"] = namespace configuration_elt["path"] = path iq_result_elt = await iq_elt.send() try: - configuration_elt = next(iq_result_elt.elements(NS_FIS_CONFIGURATION, "configuration")) + configuration_elt = next( + iq_result_elt.elements(NS_FIS_CONFIGURATION, "configuration") + ) except StopIteration: - raise exceptions.DataError(f"Invalid result to configuration request: {iq_result_elt.toXml()}") + raise exceptions.DataError( + f"Invalid result to configuration request: {iq_result_elt.toXml()}" + ) form = data_form.findForm(configuration_elt, NS_FIS_CONFIGURATION) configuration = {f.var: f.value for f in form.fields.values()} @@ -938,8 +962,11 @@ def _configuration_set(self, service_jid_s, namespace, path, configuration, profile): client = self.host.get_client(profile) service = jid.JID(service_jid_s) - return defer.ensureDeferred(self.configuration_set( - client, service, namespace or None, path, configuration)) + return defer.ensureDeferred( + self.configuration_set( + client, service, namespace or None, path, configuration + ) + ) async def configuration_set( self, @@ -952,7 +979,7 @@ if not path: raise ValueError(f"invalid path: {path!r}") iq_elt = client.IQ("set") - iq_elt['to'] = service.full() + iq_elt["to"] = service.full() configuration_elt = iq_elt.addElement((NS_FIS_CONFIGURATION, "configuration")) if namespace: configuration_elt["namespace"] = namespace @@ -968,37 +995,42 @@ async def on_component_configuration_get(self, client, iq_elt): try: - ( - from_jid, configuration_elt, path, namespace, file_data - ) = await self._parse_element(client, iq_elt, "configuration", NS_FIS_CONFIGURATION) + (from_jid, configuration_elt, path, namespace, file_data) = ( + await self._parse_element( + client, iq_elt, "configuration", NS_FIS_CONFIGURATION + ) + ) except exceptions.CancelError: return except RootPathException: - client.sendError(iq_elt, 'bad-request', "Root path can't be used") + client.sendError(iq_elt, "bad-request", "Root path can't be used") return try: - access_type = file_data['access'][C.ACCESS_PERM_READ]['type'] + access_type = file_data["access"][C.ACCESS_PERM_READ]["type"] except KeyError: - access_model = 'whitelist' + access_model = "whitelist" else: - access_model = 'open' if access_type == C.ACCESS_TYPE_PUBLIC else 'whitelist' + access_model = "open" if access_type == C.ACCESS_TYPE_PUBLIC else "whitelist" iq_result_elt = xmlstream.toResponse(iq_elt, "result") - configuration_elt = iq_result_elt.addElement((NS_FIS_CONFIGURATION, 'configuration')) + configuration_elt = iq_result_elt.addElement( + (NS_FIS_CONFIGURATION, "configuration") + ) form = data_form.Form(formType="form", formNamespace=NS_FIS_CONFIGURATION) - form.makeFields({'access_model': access_model}) + form.makeFields({"access_model": access_model}) configuration_elt.addChild(form.toElement()) client.send(iq_result_elt) async def _set_configuration(self, client, configuration_elt, file_data): form = data_form.findForm(configuration_elt, NS_FIS_CONFIGURATION) for name, value in form.items(): - if name == 'access_model': + if name == "access_model": await self.host.memory.set_file_access_model(client, file_data, value) else: # TODO: send a IQ error? log.warning( - f"Trying to set a not implemented configuration option: {name}") + f"Trying to set a not implemented configuration option: {name}" + ) def _on_component_configuration_set(self, iq_elt, client): iq_elt.handled = True @@ -1006,23 +1038,25 @@ async def on_component_configuration_set(self, client, iq_elt): try: - ( - from_jid, configuration_elt, path, namespace, file_data - ) = await self._parse_element(client, iq_elt, "configuration", NS_FIS_CONFIGURATION) + (from_jid, configuration_elt, path, namespace, file_data) = ( + await self._parse_element( + client, iq_elt, "configuration", NS_FIS_CONFIGURATION + ) + ) except exceptions.CancelError: return except RootPathException: - client.sendError(iq_elt, 'bad-request', "Root path can't be used") + client.sendError(iq_elt, "bad-request", "Root path can't be used") return from_jid_bare = from_jid.userhostJID() - is_owner = from_jid_bare == file_data.get('owner') + is_owner = from_jid_bare == file_data.get("owner") if not is_owner: log.warning( f"{from_jid} tried to modify {path} configuration while the owner is " f"{file_data['owner']}" ) - client.sendError(iq_elt, 'forbidden') + client.sendError(iq_elt, "forbidden") return await self._set_configuration(client, configuration_elt, file_data) @@ -1035,8 +1069,11 @@ def _create_dir(self, service_jid_s, namespace, path, configuration, profile): client = self.host.get_client(profile) service = jid.JID(service_jid_s) - return defer.ensureDeferred(self.create_dir( - client, service, namespace or None, path, configuration or None)) + return defer.ensureDeferred( + self.create_dir( + client, service, namespace or None, path, configuration or None + ) + ) async def create_dir( self, @@ -1049,13 +1086,15 @@ if not path: raise ValueError(f"invalid path: {path!r}") iq_elt = client.IQ("set") - iq_elt['to'] = service.full() + iq_elt["to"] = service.full() create_dir_elt = iq_elt.addElement((NS_FIS_CREATE, "dir")) if namespace: create_dir_elt["namespace"] = namespace create_dir_elt["path"] = path if configuration: - configuration_elt = create_dir_elt.addElement((NS_FIS_CONFIGURATION, "configuration")) + configuration_elt = create_dir_elt.addElement( + (NS_FIS_CONFIGURATION, "configuration") + ) form = data_form.Form(formType="submit", formNamespace=NS_FIS_CONFIGURATION) form.makeFields(configuration) configuration_elt.addChild(form.toElement()) @@ -1068,13 +1107,13 @@ async def on_component_create_dir(self, client, iq_elt): peer_jid, owner = client.get_owner_and_peer(iq_elt) if peer_jid.host not in client._file_sharing_allowed_hosts: - client.sendError(iq_elt, 'forbidden') + client.sendError(iq_elt, "forbidden") return create_dir_elt = next(iq_elt.elements(NS_FIS_CREATE, "dir")) - namespace = create_dir_elt.getAttribute('namespace') - path = Path("/", create_dir_elt['path']) + namespace = create_dir_elt.getAttribute("namespace") + path = Path("/", create_dir_elt["path"]) if len(path.parts) < 2: - client.sendError(iq_elt, 'bad-request', "Root path can't be used") + client.sendError(iq_elt, "bad-request", "Root path can't be used") return # for root directories, we check permission here if len(path.parts) == 2 and owner != peer_jid.userhostJID(): @@ -1082,7 +1121,7 @@ f"{peer_jid} is trying to create a dir at {owner}'s repository:\n" f"path: {path}\nnamespace: {namespace!r}" ) - client.sendError(iq_elt, 'forbidden', "You can't create a directory there") + client.sendError(iq_elt, "forbidden", "You can't create a directory there") return # when going further into the path, the permissions will be checked by get_files files_data = await self.host.memory.get_files( @@ -1092,18 +1131,20 @@ namespace=namespace, owner=owner, ) - if path.name in [d['name'] for d in files_data]: + if path.name in [d["name"] for d in files_data]: log.warning( f"Conflict when trying to create a directory (from: {peer_jid} " f"namespace: {namespace!r} path: {path!r})" ) client.sendError( - iq_elt, 'conflict', "there is already a file or dir at this path") + iq_elt, "conflict", "there is already a file or dir at this path" + ) return try: configuration_elt = next( - create_dir_elt.elements(NS_FIS_CONFIGURATION, 'configuration')) + create_dir_elt.elements(NS_FIS_CONFIGURATION, "configuration") + ) except StopIteration: configuration_elt = None @@ -1114,18 +1155,20 @@ type_=C.FILE_TYPE_DIRECTORY, namespace=namespace, owner=owner, - peer_jid=peer_jid + peer_jid=peer_jid, ) if configuration_elt is not None: - file_data = (await self.host.memory.get_files( - client, - peer_jid=peer_jid, - path=path.parent, - name=path.name, - namespace=namespace, - owner=owner, - ))[0] + file_data = ( + await self.host.memory.get_files( + client, + peer_jid=peer_jid, + path=path.parent, + name=path.name, + namespace=namespace, + owner=owner, + ) + )[0] await self._set_configuration(client, configuration_elt, file_data) @@ -1137,9 +1180,7 @@ def _serialize_data(self, files_data): for file_data in files_data: for key, value in file_data.items(): - file_data[key] = ( - json.dumps(value) if key in ("extra",) else str(value) - ) + file_data[key] = json.dumps(value) if key in ("extra",) else str(value) return files_data def _list_files(self, target_jid, path, extra, profile): @@ -1207,9 +1248,12 @@ idx += 1 new_name = name + "_" + str(idx) name = new_name - log.info(_( - "A directory with this name is already shared, renamed to {new_name} " - "[{profile}]".format( new_name=new_name, profile=client.profile))) + log.info( + _( + "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.fis_shared_path_new(path, name, client.profile) @@ -1236,32 +1280,34 @@ def connectionInitialized(self): if self.parent.is_component: self.xmlstream.addObserver( - IQ_FIS_REQUEST, self.plugin_parent.on_component_request, client=self.parent + IQ_FIS_REQUEST, + self.plugin_parent.on_component_request, + client=self.parent, ) self.xmlstream.addObserver( IQ_FIS_AFFILIATION_GET, self.plugin_parent._on_component_affiliations_get, - client=self.parent + client=self.parent, ) self.xmlstream.addObserver( IQ_FIS_AFFILIATION_SET, self.plugin_parent._on_component_affiliations_set, - client=self.parent + client=self.parent, ) self.xmlstream.addObserver( IQ_FIS_CONFIGURATION_GET, self.plugin_parent._on_component_configuration_get, - client=self.parent + client=self.parent, ) self.xmlstream.addObserver( IQ_FIS_CONFIGURATION_SET, self.plugin_parent._on_component_configuration_set, - client=self.parent + client=self.parent, ) self.xmlstream.addObserver( IQ_FIS_CREATE_DIR, self.plugin_parent._on_component_create_dir, - client=self.parent + client=self.parent, ) else: self.xmlstream.addObserver(