WIP DAV hierarchy
This commit is contained in:
parent
3b57d21e30
commit
4a5ae87059
3 changed files with 106 additions and 47 deletions
|
@ -206,7 +206,7 @@ impl QWrite for ResourceType {
|
||||||
async fn qwrite(&self, xml: &mut Writer<impl IWrite>) -> Result<(), QError> {
|
async fn qwrite(&self, xml: &mut Writer<impl IWrite>) -> Result<(), QError> {
|
||||||
match self {
|
match self {
|
||||||
Self::Calendar => {
|
Self::Calendar => {
|
||||||
let empty_tag = xml.create_dav_element("calendar");
|
let empty_tag = xml.create_cal_element("calendar");
|
||||||
xml.q.write_event_async(Event::Empty(empty_tag)).await
|
xml.q.write_event_async(Event::Empty(empty_tag)).await
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
|
@ -18,7 +18,7 @@ impl xml::QWrite for Disabled {
|
||||||
|
|
||||||
/// The base WebDAV
|
/// The base WebDAV
|
||||||
///
|
///
|
||||||
/// Any extension is kooh is disabled through an object we can't build
|
/// Any extension is disabled through an object we can't build
|
||||||
/// due to a private inner element.
|
/// due to a private inner element.
|
||||||
#[derive(Debug, PartialEq, Clone)]
|
#[derive(Debug, PartialEq, Clone)]
|
||||||
pub struct Core {}
|
pub struct Core {}
|
||||||
|
|
|
@ -196,20 +196,22 @@ async fn router(user: std::sync::Arc<User>, req: Request<Incoming>) -> Result<Re
|
||||||
let path = req.uri().path().to_string();
|
let path = req.uri().path().to_string();
|
||||||
let path_segments: Vec<_> = path.split("/").filter(|s| *s != "").collect();
|
let path_segments: Vec<_> = path.split("/").filter(|s| *s != "").collect();
|
||||||
let method = req.method().as_str().to_uppercase();
|
let method = req.method().as_str().to_uppercase();
|
||||||
|
let node = match Box::new(RootNode {}).fetch(&user, &path_segments) {
|
||||||
|
Ok(v) => v,
|
||||||
|
Err(e) => {
|
||||||
|
tracing::warn!(err=?e, "dav node fetch failed");
|
||||||
|
return Ok(Response::builder()
|
||||||
|
.status(404)
|
||||||
|
.body(text_body("Resource not found"))?)
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
match (method.as_str(), path_segments.as_slice()) {
|
match method.as_str() {
|
||||||
("OPTIONS", _) => return Ok(Response::builder()
|
"OPTIONS" => return Ok(Response::builder()
|
||||||
.status(200)
|
.status(200)
|
||||||
.header("DAV", "1")
|
.header("DAV", "1")
|
||||||
.body(text_body(""))?),
|
.body(text_body(""))?),
|
||||||
("PROPFIND", []) => propfind_root(user, req).await,
|
"PROPFIND" => propfind(user, req, node).await,
|
||||||
(_, [ username, ..]) if *username != user.username => return Ok(Response::builder()
|
|
||||||
.status(403)
|
|
||||||
.body(text_body("Accessing other user ressources is not allowed"))?),
|
|
||||||
("PROPFIND", [ _ ]) => propfind_home(user, &req).await,
|
|
||||||
("PROPFIND", [ _, "calendar" ]) => propfind_all_calendars(user, &req).await,
|
|
||||||
("PROPFIND", [ _, "calendar", colname ]) => propfind_this_calendar(user, &req, colname).await,
|
|
||||||
("PROPFIND", [ _, "calendar", colname, event ]) => propfind_event(user, req, colname, event).await,
|
|
||||||
_ => return Ok(Response::builder()
|
_ => return Ok(Response::builder()
|
||||||
.status(501)
|
.status(501)
|
||||||
.body(text_body("Not implemented"))?),
|
.body(text_body("Not implemented"))?),
|
||||||
|
@ -224,8 +226,7 @@ const SUPPORTED_PROPNAME: [dav::PropertyRequest<Calendar>; 2] = [
|
||||||
dav::PropertyRequest::ResourceType,
|
dav::PropertyRequest::ResourceType,
|
||||||
];
|
];
|
||||||
|
|
||||||
async fn propfind_root(user: std::sync::Arc<User>, req: Request<Incoming>) -> Result<Response<BoxBody<Bytes, std::io::Error>>> {
|
async fn propfind(user: std::sync::Arc<User>, req: Request<Incoming>, node: Box<dyn DavNode>) -> Result<Response<BoxBody<Bytes, std::io::Error>>> {
|
||||||
let node = RootNode {};
|
|
||||||
let depth = depth(&req);
|
let depth = depth(&req);
|
||||||
|
|
||||||
|
|
||||||
|
@ -258,36 +259,6 @@ async fn propfind_root(user: std::sync::Arc<User>, req: Request<Incoming>) -> Re
|
||||||
serialize(node.multistatus_val(&user, &propname, depth))
|
serialize(node.multistatus_val(&user, &propname, depth))
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn propfind_home(user: std::sync::Arc<User>, req: &Request<impl hyper::body::Body>) -> Result<Response<BoxBody<Bytes, std::io::Error>>> {
|
|
||||||
tracing::info!("user home");
|
|
||||||
Ok(Response::new(text_body("Hello World!")))
|
|
||||||
}
|
|
||||||
|
|
||||||
async fn propfind_all_calendars(user: std::sync::Arc<User>, req: &Request<impl hyper::body::Body>) -> Result<Response<BoxBody<Bytes, std::io::Error>>> {
|
|
||||||
tracing::info!("calendar");
|
|
||||||
Ok(Response::new(text_body("Hello World!")))
|
|
||||||
}
|
|
||||||
|
|
||||||
async fn propfind_this_calendar(
|
|
||||||
user: std::sync::Arc<User>,
|
|
||||||
req: &Request<Incoming>,
|
|
||||||
colname: &str
|
|
||||||
) -> Result<Response<BoxBody<Bytes, std::io::Error>>> {
|
|
||||||
tracing::info!(name=colname, "selected calendar");
|
|
||||||
Ok(Response::new(text_body("Hello World!")))
|
|
||||||
}
|
|
||||||
|
|
||||||
async fn propfind_event(
|
|
||||||
user: std::sync::Arc<User>,
|
|
||||||
req: Request<Incoming>,
|
|
||||||
colname: &str,
|
|
||||||
event: &str,
|
|
||||||
) -> Result<Response<BoxBody<Bytes, std::io::Error>>> {
|
|
||||||
tracing::info!(name=colname, obj=event, "selected event");
|
|
||||||
Ok(Response::new(text_body("Hello World!")))
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
#[allow(dead_code)]
|
#[allow(dead_code)]
|
||||||
async fn collections(_user: std::sync::Arc<User>, _req: Request<impl hyper::body::Body>) -> Result<Response<Full<Bytes>>> {
|
async fn collections(_user: std::sync::Arc<User>, _req: Request<impl hyper::body::Body>) -> Result<Response<Full<Bytes>>> {
|
||||||
unimplemented!();
|
unimplemented!();
|
||||||
|
@ -326,7 +297,7 @@ fn serialize<T: dxml::QWrite + Send + 'static>(elem: T) -> Result<Response<BoxBo
|
||||||
let sink = PollSender::new(tx).sink_map_err(|_| Error::from(ErrorKind::BrokenPipe));
|
let sink = PollSender::new(tx).sink_map_err(|_| Error::from(ErrorKind::BrokenPipe));
|
||||||
let mut writer = SinkWriter::new(CopyToBytes::new(sink));
|
let mut writer = SinkWriter::new(CopyToBytes::new(sink));
|
||||||
let q = quick_xml::writer::Writer::new_with_indent(&mut writer, b' ', 4);
|
let q = quick_xml::writer::Writer::new_with_indent(&mut writer, b' ', 4);
|
||||||
let ns_to_apply = vec![ ("xmlns:D".into(), "DAV:".into()) ];
|
let ns_to_apply = vec![ ("xmlns:D".into(), "DAV:".into()), ("xmlns:C".into(), "urn:ietf:params:xml:ns:caldav".into()) ];
|
||||||
let mut qwriter = dxml::Writer { q, ns_to_apply };
|
let mut qwriter = dxml::Writer { q, ns_to_apply };
|
||||||
let decl = quick_xml::events::BytesDecl::from_start(quick_xml::events::BytesStart::from_content("xml encoding='utf-8' version='1.0'", 0));
|
let decl = quick_xml::events::BytesDecl::from_start(quick_xml::events::BytesStart::from_content("xml encoding='utf-8' version='1.0'", 0));
|
||||||
match qwriter.q.write_event_async(quick_xml::events::Event::Decl(decl)).await {
|
match qwriter.q.write_event_async(quick_xml::events::Event::Decl(decl)).await {
|
||||||
|
@ -369,16 +340,22 @@ async fn deserialize<T: dxml::Node<T>>(req: Request<Incoming>) -> Result<T> {
|
||||||
//---
|
//---
|
||||||
|
|
||||||
type ArcUser = std::sync::Arc<User>;
|
type ArcUser = std::sync::Arc<User>;
|
||||||
trait DavNode {
|
trait DavNode: Send {
|
||||||
|
// ------- specialized logic
|
||||||
|
|
||||||
// recurence
|
// recurence
|
||||||
fn children(&self, user: &ArcUser) -> Vec<Box<dyn DavNode>>;
|
fn children(&self, user: &ArcUser) -> Vec<Box<dyn DavNode>>;
|
||||||
|
fn fetch(self: Box<Self>, user: &ArcUser, path: &[&str]) -> Result<Box<dyn DavNode>>;
|
||||||
|
|
||||||
// node properties
|
// node properties
|
||||||
|
fn path(&self, user: &ArcUser) -> String;
|
||||||
fn name(&self, user: &ArcUser) -> String;
|
fn name(&self, user: &ArcUser) -> String;
|
||||||
fn supported_properties(&self, user: &ArcUser) -> dav::PropName<Calendar>;
|
fn supported_properties(&self, user: &ArcUser) -> dav::PropName<Calendar>;
|
||||||
fn properties(&self, user: &ArcUser, props: &dav::PropName<Calendar>) -> dav::PropValue<Calendar>;
|
fn properties(&self, user: &ArcUser, props: &dav::PropName<Calendar>) -> dav::PropValue<Calendar>;
|
||||||
|
|
||||||
// building DAV responses
|
// ----- common
|
||||||
|
|
||||||
|
/// building DAV responses
|
||||||
fn multistatus_name(&self, user: &ArcUser, depth: dav::Depth) -> dav::Multistatus<Calendar, dav::PropName<Calendar>> {
|
fn multistatus_name(&self, user: &ArcUser, depth: dav::Depth) -> dav::Multistatus<Calendar, dav::PropName<Calendar>> {
|
||||||
let mut names = vec![(".".into(), self.supported_properties(user))];
|
let mut names = vec![(".".into(), self.supported_properties(user))];
|
||||||
if matches!(depth, dav::Depth::One | dav::Depth::Infinity) {
|
if matches!(depth, dav::Depth::One | dav::Depth::Infinity) {
|
||||||
|
@ -436,6 +413,23 @@ trait DavNode {
|
||||||
|
|
||||||
struct RootNode {}
|
struct RootNode {}
|
||||||
impl DavNode for RootNode {
|
impl DavNode for RootNode {
|
||||||
|
fn fetch(self: Box<Self>, user: &ArcUser, path: &[&str]) -> Result<Box<dyn DavNode>> {
|
||||||
|
if path.len() == 0 {
|
||||||
|
return Ok(self)
|
||||||
|
}
|
||||||
|
|
||||||
|
if path[0] == user.username {
|
||||||
|
let child = Box::new(HomeNode {});
|
||||||
|
return child.fetch(user, &path[1..])
|
||||||
|
}
|
||||||
|
|
||||||
|
Err(anyhow!("Not found"))
|
||||||
|
}
|
||||||
|
|
||||||
|
fn path(&self, user: &ArcUser) -> String {
|
||||||
|
todo!();
|
||||||
|
}
|
||||||
|
|
||||||
fn name(&self, _user: &ArcUser) -> String {
|
fn name(&self, _user: &ArcUser) -> String {
|
||||||
"/".into()
|
"/".into()
|
||||||
}
|
}
|
||||||
|
@ -459,6 +453,23 @@ impl DavNode for RootNode {
|
||||||
|
|
||||||
struct HomeNode {}
|
struct HomeNode {}
|
||||||
impl DavNode for HomeNode {
|
impl DavNode for HomeNode {
|
||||||
|
fn fetch(self: Box<Self>, user: &ArcUser, path: &[&str]) -> Result<Box<dyn DavNode>> {
|
||||||
|
if path.len() == 0 {
|
||||||
|
return Ok(self)
|
||||||
|
}
|
||||||
|
|
||||||
|
if path[0] == "calendar" {
|
||||||
|
let child = Box::new(CalendarListNode {});
|
||||||
|
return child.fetch(user, &path[1..])
|
||||||
|
}
|
||||||
|
|
||||||
|
Err(anyhow!("Not found"))
|
||||||
|
}
|
||||||
|
|
||||||
|
fn path(&self, user: &ArcUser) -> String {
|
||||||
|
todo!();
|
||||||
|
}
|
||||||
|
|
||||||
fn name(&self, user: &ArcUser) -> String {
|
fn name(&self, user: &ArcUser) -> String {
|
||||||
format!("{}/", user.username)
|
format!("{}/", user.username)
|
||||||
}
|
}
|
||||||
|
@ -482,6 +493,24 @@ impl DavNode for HomeNode {
|
||||||
|
|
||||||
struct CalendarListNode {}
|
struct CalendarListNode {}
|
||||||
impl DavNode for CalendarListNode {
|
impl DavNode for CalendarListNode {
|
||||||
|
fn fetch(self: Box<Self>, user: &ArcUser, path: &[&str]) -> Result<Box<dyn DavNode>> {
|
||||||
|
if path.len() == 0 {
|
||||||
|
return Ok(self)
|
||||||
|
}
|
||||||
|
|
||||||
|
//@FIXME hardcoded logic
|
||||||
|
if path[0] == "personal" {
|
||||||
|
let child = Box::new(CalendarNode { name: "personal".to_string() });
|
||||||
|
return child.fetch(user, &path[1..])
|
||||||
|
}
|
||||||
|
|
||||||
|
Err(anyhow!("Not found"))
|
||||||
|
}
|
||||||
|
|
||||||
|
fn path(&self, user: &ArcUser) -> String {
|
||||||
|
todo!();
|
||||||
|
}
|
||||||
|
|
||||||
fn name(&self, _user: &ArcUser) -> String {
|
fn name(&self, _user: &ArcUser) -> String {
|
||||||
"calendar/".into()
|
"calendar/".into()
|
||||||
}
|
}
|
||||||
|
@ -507,6 +536,24 @@ struct CalendarNode {
|
||||||
name: String,
|
name: String,
|
||||||
}
|
}
|
||||||
impl DavNode for CalendarNode {
|
impl DavNode for CalendarNode {
|
||||||
|
fn fetch(self: Box<Self>, user: &ArcUser, path: &[&str]) -> Result<Box<dyn DavNode>> {
|
||||||
|
if path.len() == 0 {
|
||||||
|
return Ok(self)
|
||||||
|
}
|
||||||
|
|
||||||
|
//@FIXME hardcoded logic
|
||||||
|
if path[0] == "something.ics" {
|
||||||
|
let child = Box::new(EventNode { file: "something.ics".to_string() });
|
||||||
|
return child.fetch(user, &path[1..])
|
||||||
|
}
|
||||||
|
|
||||||
|
Err(anyhow!("Not found"))
|
||||||
|
}
|
||||||
|
|
||||||
|
fn path(&self, user: &ArcUser) -> String {
|
||||||
|
todo!();
|
||||||
|
}
|
||||||
|
|
||||||
fn name(&self, _user: &ArcUser) -> String {
|
fn name(&self, _user: &ArcUser) -> String {
|
||||||
format!("{}/", self.name)
|
format!("{}/", self.name)
|
||||||
}
|
}
|
||||||
|
@ -535,6 +582,18 @@ struct EventNode {
|
||||||
file: String,
|
file: String,
|
||||||
}
|
}
|
||||||
impl DavNode for EventNode {
|
impl DavNode for EventNode {
|
||||||
|
fn fetch(self: Box<Self>, user: &ArcUser, path: &[&str]) -> Result<Box<dyn DavNode>> {
|
||||||
|
if path.len() == 0 {
|
||||||
|
return Ok(self)
|
||||||
|
}
|
||||||
|
|
||||||
|
Err(anyhow!("Not found"))
|
||||||
|
}
|
||||||
|
|
||||||
|
fn path(&self, user: &ArcUser) -> String {
|
||||||
|
todo!();
|
||||||
|
}
|
||||||
|
|
||||||
fn name(&self, _user: &ArcUser) -> String {
|
fn name(&self, _user: &ArcUser) -> String {
|
||||||
self.file.to_string()
|
self.file.to_string()
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue