use futures_util::{FutureExt, StreamExt, future::BoxFuture};
use headers::HeaderMapExt;
use http::{Request, Response, StatusCode};
use crate::async_stream::AsyncStream;
use crate::body::Body;
use crate::conditional::if_match_get_tokens;
use crate::davheaders::Depth;
use crate::davpath::DavPath;
use crate::errors::*;
use crate::fs::*;
use crate::multierror::{MultiError, multi_error};
use crate::{DavInner, DavResult};
async fn add_status<'a>(m_err: &'a mut MultiError, path: &'a DavPath, e: FsError) -> DavError {
let status = DavError::FsError(e).statuscode();
if let Err(x) = m_err.add_status(path, status).await {
return x.into();
}
DavError::Status(status)
}
async fn dir_status<'a>(res: &'a mut MultiError, path: &'a DavPath, e: FsError) -> DavError {
let status = match e {
FsError::Exists => StatusCode::CONFLICT,
e => DavError::FsError(e).statuscode(),
};
if let Err(x) = res.add_status(path, status).await {
return x.into();
}
DavError::Status(status)
}
impl<C: Clone + Send + Sync + 'static> DavInner<C> {
pub(crate) fn delete_items<'a>(
&'a self,
res: &'a mut MultiError,
depth: Depth,
meta: Box<dyn DavMetaData + 'a>,
path: &'a DavPath,
) -> BoxFuture<'a, DavResult<()>> {
async move {
if !meta.is_dir() {
trace!("delete_items (file) {path} {depth:?}");
return match self.fs.remove_file(path, &self.credentials).await {
Ok(x) => Ok(x),
Err(e) => Err(add_status(res, path, e).await),
};
}
if depth == Depth::Zero {
trace!("delete_items (dir) {path} {depth:?}");
return match self.fs.remove_dir(path, &self.credentials).await {
Ok(x) => Ok(x),
Err(e) => Err(add_status(res, path, e).await),
};
}
let mut entries = match self
.fs
.read_dir(path, ReadDirMeta::DataSymlink, &self.credentials)
.await
{
Ok(x) => Ok(x),
Err(e) => Err(add_status(res, path, e).await),
}?;
let mut result = Ok(());
while let Some(dirent) = entries.next().await {
let dirent = match dirent {
Ok(dirent) => dirent,
Err(e) => {
result = Err(add_status(res, path, e).await);
continue;
}
};
let meta = match dirent.metadata().await {
Ok(m) => m,
Err(e) => {
result = Err(add_status(res, path, e).await);
continue;
}
};
let mut npath = path.clone();
npath.push_segment(&dirent.name());
npath.add_slash_if(meta.is_dir());
if let Err(e) = self.delete_items(res, depth, meta, &npath).await {
match e {
DavError::Status(_) => {
result = Err(e);
continue;
}
_ => return Err(e),
}
}
}
result?;
match self.fs.remove_dir(path, &self.credentials).await {
Ok(x) => Ok(x),
Err(e) => Err(dir_status(res, path, e).await),
}
}
.boxed()
}
pub(crate) async fn handle_delete(self, req: &Request<()>) -> DavResult<Response<Body>> {
let depth = match req.headers().typed_get::<Depth>() {
Some(Depth::Infinity) | None => Depth::Infinity,
Some(Depth::Zero) => Depth::Zero,
_ => return Err(DavError::Status(StatusCode::BAD_REQUEST)),
};
let mut path = self.path(req);
let meta = self.fs.symlink_metadata(&path, &self.credentials).await?;
if meta.is_symlink()
&& let Ok(m2) = self.fs.metadata(&path, &self.credentials).await
{
path.add_slash_if(m2.is_dir());
}
path.add_slash_if(meta.is_dir());
let tokens_res = if_match_get_tokens(
req,
Some(meta.as_ref()),
self.fs.as_ref(),
&self.ls,
&path,
&self.credentials,
)
.await;
let tokens = match tokens_res {
Ok(t) => t,
Err(s) => return Err(DavError::Status(s)),
};
if let Some(ref locksystem) = self.ls {
let principal = self.principal.as_deref();
if let Err(_l) = locksystem
.check(&path, principal, false, true, &tokens)
.await
{
return Err(DavError::Status(StatusCode::LOCKED));
}
}
let req_path = path.clone();
let items = AsyncStream::new(|tx| {
async move {
let mut multierror = MultiError::new(tx);
let fut = self.delete_items(&mut multierror, depth, meta, &path);
if let Ok(()) = fut.await {
if let Some(ref locksystem) = self.ls {
locksystem.delete(&path).await.ok();
}
let _ = multierror.add_status(&path, StatusCode::NO_CONTENT).await;
}
Ok(())
}
});
multi_error(req_path, items).await
}
}