mirror of
https://github.com/lennart-k/rustical.git
synced 2025-12-13 22:52:22 +00:00
@@ -1,4 +1,8 @@
|
||||
use axum::{body::Body, extract::FromRequestParts, response::IntoResponse};
|
||||
use axum::{
|
||||
body::Body,
|
||||
extract::{FromRequestParts, OptionalFromRequestParts},
|
||||
response::IntoResponse,
|
||||
};
|
||||
use rustical_xml::{ValueDeserialize, ValueSerialize, XmlError};
|
||||
use thiserror::Error;
|
||||
|
||||
@@ -59,6 +63,21 @@ impl TryFrom<&[u8]> for Depth {
|
||||
}
|
||||
}
|
||||
|
||||
impl<S: Send + Sync> OptionalFromRequestParts<S> for Depth {
|
||||
type Rejection = InvalidDepthHeader;
|
||||
|
||||
async fn from_request_parts(
|
||||
parts: &mut axum::http::request::Parts,
|
||||
_state: &S,
|
||||
) -> Result<Option<Self>, Self::Rejection> {
|
||||
if let Some(depth_header) = parts.headers.get("Depth") {
|
||||
Ok(Some(depth_header.as_bytes().try_into()?))
|
||||
} else {
|
||||
Ok(None)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<S: Send + Sync> FromRequestParts<S> for Depth {
|
||||
type Rejection = InvalidDepthHeader;
|
||||
|
||||
|
||||
@@ -1,9 +1,19 @@
|
||||
use axum::{body::Body, extract::FromRequestParts, response::IntoResponse};
|
||||
use thiserror::Error;
|
||||
|
||||
#[derive(Error, Debug)]
|
||||
#[error("Invalid Overwrite header")]
|
||||
pub struct InvalidOverwriteHeader;
|
||||
|
||||
impl IntoResponse for InvalidOverwriteHeader {
|
||||
fn into_response(self) -> axum::response::Response {
|
||||
axum::response::Response::builder()
|
||||
.status(axum::http::StatusCode::BAD_REQUEST)
|
||||
.body(Body::new("Invalid Overwrite header".to_string()))
|
||||
.expect("this always works")
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, PartialEq, Default)]
|
||||
pub enum Overwrite {
|
||||
#[default]
|
||||
@@ -17,6 +27,21 @@ impl Overwrite {
|
||||
}
|
||||
}
|
||||
|
||||
impl<S: Send + Sync> FromRequestParts<S> for Overwrite {
|
||||
type Rejection = InvalidOverwriteHeader;
|
||||
|
||||
async fn from_request_parts(
|
||||
parts: &mut axum::http::request::Parts,
|
||||
_state: &S,
|
||||
) -> Result<Self, Self::Rejection> {
|
||||
if let Some(overwrite_header) = parts.headers.get("Overwrite") {
|
||||
overwrite_header.as_bytes().try_into()
|
||||
} else {
|
||||
Ok(Self::default())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl TryFrom<&[u8]> for Overwrite {
|
||||
type Error = InvalidOverwriteHeader;
|
||||
|
||||
|
||||
@@ -38,16 +38,6 @@ pub trait AxumMethods: Sized + Send + Sync + 'static {
|
||||
None
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn copy() -> Option<MethodFunction<Self>> {
|
||||
None
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn mv() -> Option<MethodFunction<Self>> {
|
||||
None
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn put() -> Option<MethodFunction<Self>> {
|
||||
None
|
||||
@@ -58,6 +48,8 @@ pub trait AxumMethods: Sized + Send + Sync + 'static {
|
||||
let mut allow = vec![
|
||||
Method::from_str("PROPFIND").unwrap(),
|
||||
Method::from_str("PROPPATCH").unwrap(),
|
||||
Method::from_str("COPY").unwrap(),
|
||||
Method::from_str("MOVE").unwrap(),
|
||||
Method::DELETE,
|
||||
Method::OPTIONS,
|
||||
];
|
||||
@@ -79,12 +71,6 @@ pub trait AxumMethods: Sized + Send + Sync + 'static {
|
||||
if Self::mkcalendar().is_some() {
|
||||
allow.push(Method::from_str("MKCALENDAR").unwrap());
|
||||
}
|
||||
if Self::copy().is_some() {
|
||||
allow.push(Method::from_str("COPY").unwrap());
|
||||
}
|
||||
if Self::mv().is_some() {
|
||||
allow.push(Method::from_str("MOVE").unwrap());
|
||||
}
|
||||
if Self::put().is_some() {
|
||||
allow.push(Method::PUT);
|
||||
}
|
||||
|
||||
@@ -1,5 +1,9 @@
|
||||
use super::methods::{axum_route_propfind, axum_route_proppatch};
|
||||
use crate::resource::{ResourceService, axum_methods::AxumMethods};
|
||||
use crate::resource::{
|
||||
ResourceService,
|
||||
axum_methods::AxumMethods,
|
||||
methods::{axum_route_copy, axum_route_move},
|
||||
};
|
||||
use axum::{
|
||||
body::Body,
|
||||
extract::FromRequestParts,
|
||||
@@ -51,12 +55,18 @@ where
|
||||
Handler::with_state(axum_route_proppatch::<RS>, self.resource_service.clone());
|
||||
let mut delete_service =
|
||||
Handler::with_state(axum_route_delete::<RS>, self.resource_service.clone());
|
||||
let mut move_service =
|
||||
Handler::with_state(axum_route_move::<RS>, self.resource_service.clone());
|
||||
let mut copy_service =
|
||||
Handler::with_state(axum_route_copy::<RS>, self.resource_service.clone());
|
||||
let mut options_service = Handler::with_state(route_options::<RS>, ());
|
||||
match req.method().as_str() {
|
||||
"PROPFIND" => return Box::pin(Service::call(&mut propfind_service, req)),
|
||||
"PROPPATCH" => return Box::pin(Service::call(&mut proppatch_service, req)),
|
||||
"DELETE" => return Box::pin(Service::call(&mut delete_service, req)),
|
||||
"OPTIONS" => return Box::pin(Service::call(&mut options_service, req)),
|
||||
"MOVE" => return Box::pin(Service::call(&mut move_service, req)),
|
||||
"COPY" => return Box::pin(Service::call(&mut copy_service, req)),
|
||||
"REPORT" => {
|
||||
if let Some(svc) = RS::report() {
|
||||
return svc(self.resource_service.clone(), req);
|
||||
@@ -87,16 +97,6 @@ where
|
||||
return svc(self.resource_service.clone(), req);
|
||||
}
|
||||
}
|
||||
"COPY" => {
|
||||
if let Some(svc) = RS::copy() {
|
||||
return svc(self.resource_service.clone(), req);
|
||||
}
|
||||
}
|
||||
"MOVE" => {
|
||||
if let Some(svc) = RS::mv() {
|
||||
return svc(self.resource_service.clone(), req);
|
||||
}
|
||||
}
|
||||
"PUT" => {
|
||||
if let Some(svc) = RS::put() {
|
||||
return svc(self.resource_service.clone(), req);
|
||||
|
||||
25
crates/dav/src/resource/methods/copy.rs
Normal file
25
crates/dav/src/resource/methods/copy.rs
Normal file
@@ -0,0 +1,25 @@
|
||||
use axum::{
|
||||
extract::{Path, State},
|
||||
response::{IntoResponse, Response},
|
||||
};
|
||||
use http::StatusCode;
|
||||
use tracing::instrument;
|
||||
|
||||
use crate::{
|
||||
header::{Depth, Overwrite},
|
||||
resource::ResourceService,
|
||||
};
|
||||
|
||||
#[instrument(skip(_path, _resource_service,))]
|
||||
pub(crate) async fn axum_route_copy<R: ResourceService>(
|
||||
Path(_path): Path<R::PathComponents>,
|
||||
State(_resource_service): State<R>,
|
||||
depth: Option<Depth>,
|
||||
principal: R::Principal,
|
||||
overwrite: Overwrite,
|
||||
) -> Result<Response, R::Error> {
|
||||
// TODO: Actually implement, but to be WebDAV-compliant we must at least support this route but
|
||||
// can return a 403 error
|
||||
let _depth = depth.unwrap_or(Depth::Infinity);
|
||||
Ok(StatusCode::FORBIDDEN.into_response())
|
||||
}
|
||||
@@ -1,7 +1,11 @@
|
||||
mod copy;
|
||||
mod delete;
|
||||
mod mv;
|
||||
mod propfind;
|
||||
mod proppatch;
|
||||
|
||||
pub(crate) use copy::axum_route_copy;
|
||||
pub(crate) use delete::axum_route_delete;
|
||||
pub(crate) use mv::axum_route_move;
|
||||
pub(crate) use propfind::axum_route_propfind;
|
||||
pub(crate) use proppatch::axum_route_proppatch;
|
||||
|
||||
25
crates/dav/src/resource/methods/mv.rs
Normal file
25
crates/dav/src/resource/methods/mv.rs
Normal file
@@ -0,0 +1,25 @@
|
||||
use axum::{
|
||||
extract::{Path, State},
|
||||
response::{IntoResponse, Response},
|
||||
};
|
||||
use http::StatusCode;
|
||||
use tracing::instrument;
|
||||
|
||||
use crate::{
|
||||
header::{Depth, Overwrite},
|
||||
resource::ResourceService,
|
||||
};
|
||||
|
||||
#[instrument(skip(_path, _resource_service,))]
|
||||
pub(crate) async fn axum_route_move<R: ResourceService>(
|
||||
Path(_path): Path<R::PathComponents>,
|
||||
State(_resource_service): State<R>,
|
||||
depth: Option<Depth>,
|
||||
principal: R::Principal,
|
||||
overwrite: Overwrite,
|
||||
) -> Result<Response, R::Error> {
|
||||
// TODO: Actually implement, but to be WebDAV-compliant we must at least support this route but
|
||||
// can return a 403 error
|
||||
let _depth = depth.unwrap_or(Depth::Infinity);
|
||||
Ok(StatusCode::FORBIDDEN.into_response())
|
||||
}
|
||||
Reference in New Issue
Block a user