plugin XEP-0329: added FISCreateDir method:

This method is used to create a directory for component file sharing, and optionally set
its configuration.
This commit is contained in:
Goffi 2020-09-17 16:42:00 +02:00
commit f321952cbe
4 changed files with 136 additions and 16 deletions

View file

@ -391,8 +391,6 @@ class FileSharing:
client._file_sharing_base_url = f"https://{client.host}:{self.http_port}"
else:
client._file_sharing_base_url = public_base_url
client._file_sharing_allowed_hosts = self.host.memory.getConfig(
'component file_sharing', 'http_upload_allowed_hosts_list') or [client.host]
path = client.file_tmp_dir = os.path.join(
self.host.memory.getConfig("", "local_dir"),
C.FILES_TMP_DIR,
@ -596,6 +594,7 @@ class FileSharing:
def _onHTTPUpload(self, client, request):
# filename should be already cleaned, but it's better to double check
assert '/' not in request.filename
# client._file_sharing_allowed_hosts is set in plugin XEP-0329
if request.from_.host not in client._file_sharing_allowed_hosts:
raise error.StanzaError("forbidden")

View file

@ -1,7 +1,6 @@
#!/usr/bin/env python3
# SAT plugin to detect language (experimental)
# SàT plugin to manage invitations
# Copyright (C) 2009-2020 Jérôme Poisson (goffi@goffi.org)
# This program is free software: you can redistribute it and/or modify

View file

@ -87,6 +87,7 @@ class XEP_0313(object):
if not last_mess:
log.info(_("It seems that we have no MAM history yet"))
stanza_id = None
# FIXME: we should restrict starting of the archive, as it can be huge
else:
stanza_id = last_mess[0][-1]['stanza_id']
rsm_req = rsm.RSMRequest(max_=100, after=stanza_id)

View file

@ -53,6 +53,7 @@ PLUGIN_INFO = {
NS_FIS = "urn:xmpp:fis:0"
NS_FIS_AFFILIATION = "org.salut-a-toi.fis-affiliation"
NS_FIS_CONFIGURATION = "org.salut-a-toi.fis-configuration"
NS_FIS_CREATE = "org.salut-a-toi.fis-create"
IQ_FIS_REQUEST = f'{C.IQ_GET}/query[@xmlns="{NS_FIS}"]'
# not in the standard, but needed, and it's handy to keep it here
@ -60,6 +61,7 @@ IQ_FIS_AFFILIATION_GET = f'{C.IQ_GET}/affiliations[@xmlns="{NS_FIS_AFFILIATION}"
IQ_FIS_AFFILIATION_SET = f'{C.IQ_SET}/affiliations[@xmlns="{NS_FIS_AFFILIATION}"]'
IQ_FIS_CONFIGURATION_GET = f'{C.IQ_GET}/configuration[@xmlns="{NS_FIS_CONFIGURATION}"]'
IQ_FIS_CONFIGURATION_SET = f'{C.IQ_SET}/configuration[@xmlns="{NS_FIS_CONFIGURATION}"]'
IQ_FIS_CREATE_DIR = f'{C.IQ_SET}/dir[@xmlns="{NS_FIS_CREATE}"]'
SINGLE_FILES_DIR = "files"
TYPE_VIRTUAL = "virtual"
TYPE_PATH = "path"
@ -336,6 +338,14 @@ class XEP_0329(object):
method=self._configurationSet,
async_=True,
)
host.bridge.addMethod(
"FISCreateDir",
".plugin",
in_sign="sssa{ss}s",
out_sign="",
method=self._createDir,
async_=True,
)
host.bridge.addSignal("FISSharedPathNew", ".plugin", signature="sss")
host.bridge.addSignal("FISSharedPathRemoved", ".plugin", signature="ss")
host.trigger.add("XEP-0234_fileSendingRequest", self._fileSendingRequestTrigger)
@ -345,7 +355,10 @@ class XEP_0329(object):
return XEP_0329_handler(self)
def profileConnected(self, client):
if not client.is_component:
if client.is_component:
client._file_sharing_allowed_hosts = self.host.memory.getConfig(
'component file_sharing', 'http_upload_allowed_hosts_list') or [client.host]
else:
client._XEP_0329_root_node = ShareNode(
None,
None,
@ -726,7 +739,7 @@ class XEP_0329(object):
if len(path.parts) < 2:
raise RootPathException
namespace = elt.getAttribute('namespace')
files_data = await self.host.memory.getFiles(
files_data = await self.host.memory.getfiles(
client,
peer_jid=from_jid,
path=str(path.parent),
@ -884,7 +897,7 @@ class XEP_0329(object):
return
except Exception as e:
log.error(
f"unexepected exception while setting affiliation element: {e}\n"
f"unexepected exception while setting affiliation element: {e}\n"
f"{affiliations_elt.toXml()}"
)
client.sendError(iq_elt, 'internal-server-error', f"{e}")
@ -947,7 +960,7 @@ class XEP_0329(object):
service: jid.JID,
namespace: Optional[str],
path: str,
configuration: Dict[jid.JID, str],
configuration: Dict[str, str],
):
if not path:
raise ValueError(f"invalid path: {path!r}")
@ -990,6 +1003,16 @@ class XEP_0329(object):
configuration_elt.addChild(form.toElement())
client.send(iq_result_elt)
async def _setConfiguration(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':
await self.host.memory.setFileAccessModel(client, file_data, value)
else:
# TODO: send a IQ error?
log.warning(
f"Trying to set a not implemented configuration option: {name}")
def _onComponentConfigurationSet(self, iq_elt, client):
iq_elt.handled = True
defer.ensureDeferred(self.onComponentConfigurationSet(client, iq_elt))
@ -1015,14 +1038,107 @@ class XEP_0329(object):
client.sendError(iq_elt, 'forbidden')
return
form = data_form.findForm(configuration_elt, NS_FIS_CONFIGURATION)
for name, value in form.items():
if name == 'access_model':
await self.host.memory.setFileAccessModel(client, file_data, value)
else:
# TODO: send a IQ error?
log.warning(
f"Trying to set a not implemented configuration option: {name}")
await self._setConfiguration(client, configuration_elt, file_data)
iq_result_elt = xmlstream.toResponse(iq_elt, "result")
client.send(iq_result_elt)
# directory creation
def _createDir(self, service_jid_s, namespace, path, configuration, profile):
client = self.host.getClient(profile)
service = jid.JID(service_jid_s)
return defer.ensureDeferred(self.createDir(
client, service, namespace or None, path, configuration or None))
async def createDir(
self,
client: SatXMPPEntity,
service: jid.JID,
namespace: Optional[str],
path: str,
configuration: Optional[Dict[str, str]],
):
if not path:
raise ValueError(f"invalid path: {path!r}")
iq_elt = client.IQ("set")
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"))
form = data_form.Form(formType="submit", formNamespace=NS_FIS_CONFIGURATION)
form.makeFields(configuration)
configuration_elt.addChild(form.toElement())
await iq_elt.send()
def _onComponentCreateDir(self, iq_elt, client):
iq_elt.handled = True
defer.ensureDeferred(self.onComponentCreateDir(client, iq_elt))
async def onComponentCreateDir(self, client, iq_elt):
peer_jid, owner = self._compParseJids(client, iq_elt)
if peer_jid.host not in client._file_sharing_allowed_hosts:
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'])
if len(path.parts) < 2:
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():
log.warning(
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")
return
# when going further into the path, the permissions will be checked by getFiles
try:
await self.host.memory.getFiles(
client,
peer_jid=peer_jid,
path=path.parent,
name=path.name,
namespace=namespace,
owner=owner,
)
except exceptions.NotFound:
# this is expected, nothing should be found at this path
pass
else:
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")
return
try:
configuration_elt = next(
create_dir_elt.elements(NS_FIS_CONFIGURATION, 'configuration'))
except StopIteration:
configuration_elt = None
await self.host.memory.setFile(client, path.name, path=path.parent, namespace=namespace, owner=owner, peer_jid=peer_jid)
if configuration_elt is not None:
file_data = (await self.host.memory.getFiles(
client,
peer_jid=peer_jid,
path=path.parent,
name=path.name,
namespace=namespace,
owner=owner,
))[0]
await self._setConfiguration(client, configuration_elt, file_data)
iq_result_elt = xmlstream.toResponse(iq_elt, "result")
client.send(iq_result_elt)
@ -1152,6 +1268,11 @@ class XEP_0329_handler(xmlstream.XMPPHandler):
self.plugin_parent._onComponentConfigurationSet,
client=self.parent
)
self.xmlstream.addObserver(
IQ_FIS_CREATE_DIR,
self.plugin_parent._onComponentCreateDir,
client=self.parent
)
else:
self.xmlstream.addObserver(
IQ_FIS_REQUEST, self.plugin_parent.onRequest, client=self.parent