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:
parent
a82f7108a2
commit
f321952cbe
4 changed files with 136 additions and 16 deletions
|
|
@ -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")
|
||||
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
Loading…
Reference in a new issue