mirror of
https://github.com/lennart-k/rustical.git
synced 2025-12-14 19:32:29 +00:00
Move Resource and xml_snippets to dav crate
This commit is contained in:
@@ -4,4 +4,8 @@ version = "0.1.0"
|
||||
edition = "2021"
|
||||
|
||||
[dependencies]
|
||||
actix-web = "4.4.0"
|
||||
anyhow = "1.0.75"
|
||||
async-trait = "0.1.73"
|
||||
quick-xml = "0.30.0"
|
||||
rustical_auth = { path = "../auth/" }
|
||||
|
||||
@@ -1 +1,3 @@
|
||||
pub mod namespace;
|
||||
pub mod resource;
|
||||
pub mod xml_snippets;
|
||||
|
||||
70
crates/dav/src/resource.rs
Normal file
70
crates/dav/src/resource.rs
Normal file
@@ -0,0 +1,70 @@
|
||||
use std::io::Write;
|
||||
|
||||
use actix_web::{http::StatusCode, HttpRequest};
|
||||
use anyhow::{anyhow, Result};
|
||||
use async_trait::async_trait;
|
||||
use quick_xml::Writer;
|
||||
use rustical_auth::AuthInfo;
|
||||
|
||||
use crate::xml_snippets::{write_invalid_props_response, write_propstat_response};
|
||||
|
||||
// A resource is identified by a URI and has properties
|
||||
// A resource can also be a collection
|
||||
// A resource cannot be none, only Methods like PROPFIND, GET, REPORT, etc. can be exposed
|
||||
// A resource exists
|
||||
#[async_trait(?Send)]
|
||||
pub trait Resource: Sized {
|
||||
type MemberType: Resource;
|
||||
type UriComponents: Sized; // defines how the resource URI maps to parameters, i.e. /{principal}/{calendar} -> (String, String)
|
||||
|
||||
async fn acquire_from_request(
|
||||
req: HttpRequest,
|
||||
auth_info: AuthInfo,
|
||||
uri_components: Self::UriComponents,
|
||||
prefix: String,
|
||||
) -> Result<Self>;
|
||||
|
||||
fn get_path(&self) -> &str;
|
||||
async fn get_members(&self) -> Result<Vec<Self::MemberType>>;
|
||||
|
||||
fn list_dead_props() -> Vec<&'static str>;
|
||||
fn write_prop<W: Write>(&self, writer: &mut Writer<W>, prop: &str) -> Result<()>;
|
||||
}
|
||||
|
||||
pub trait HandlePropfind {
|
||||
fn propfind(&self, props: Vec<&str>) -> Result<String>;
|
||||
}
|
||||
|
||||
impl<R: Resource> HandlePropfind for R {
|
||||
fn propfind(&self, props: Vec<&str>) -> Result<String> {
|
||||
let mut props = props;
|
||||
if props.contains(&"allprops") {
|
||||
if props.len() != 1 {
|
||||
// allprops MUST be the only queried prop per spec
|
||||
return Err(anyhow!("allprops MUST be the only queried prop"));
|
||||
}
|
||||
props = R::list_dead_props();
|
||||
}
|
||||
|
||||
let mut invalid_props = Vec::<&str>::new();
|
||||
|
||||
let mut output_buffer = Vec::new();
|
||||
let mut writer = Writer::new_with_indent(&mut output_buffer, b' ', 2);
|
||||
|
||||
write_propstat_response(&mut writer, self.get_path(), StatusCode::OK, |writer| {
|
||||
for prop in props {
|
||||
// TODO: Fix error types
|
||||
match self
|
||||
.write_prop(writer, prop)
|
||||
.map_err(|_e| quick_xml::Error::TextNotFound)
|
||||
{
|
||||
Ok(_) => {}
|
||||
Err(_) => invalid_props.push(prop),
|
||||
};
|
||||
}
|
||||
Ok(())
|
||||
})?;
|
||||
write_invalid_props_response(&mut writer, self.get_path(), invalid_props)?;
|
||||
Ok(std::str::from_utf8(&output_buffer)?.to_string())
|
||||
}
|
||||
}
|
||||
109
crates/dav/src/xml_snippets.rs
Normal file
109
crates/dav/src/xml_snippets.rs
Normal file
@@ -0,0 +1,109 @@
|
||||
use std::io::Write;
|
||||
|
||||
use actix_web::http::StatusCode;
|
||||
use anyhow::Result;
|
||||
use quick_xml::{
|
||||
events::{attributes::Attribute, BytesText},
|
||||
Writer,
|
||||
};
|
||||
|
||||
pub fn write_resourcetype<W: Write>(
|
||||
writer: &mut Writer<W>,
|
||||
types: Vec<&str>,
|
||||
) -> Result<(), quick_xml::Error> {
|
||||
writer
|
||||
.create_element("resourcetype")
|
||||
.write_inner_content(|writer| {
|
||||
for resourcetype in types {
|
||||
writer.create_element(resourcetype).write_empty()?;
|
||||
}
|
||||
Ok(())
|
||||
})?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn write_invalid_props_response<W: Write>(
|
||||
writer: &mut Writer<W>,
|
||||
href: &str,
|
||||
invalid_props: Vec<&str>,
|
||||
) -> Result<(), quick_xml::Error> {
|
||||
if invalid_props.is_empty() {
|
||||
return Ok(());
|
||||
};
|
||||
|
||||
write_propstat_response(writer, href, StatusCode::NOT_FOUND, |writer| {
|
||||
for prop in invalid_props {
|
||||
writer.create_element(prop).write_empty()?;
|
||||
}
|
||||
Ok(())
|
||||
})?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn write_propstat_element<F, W: Write>(
|
||||
writer: &mut Writer<W>,
|
||||
status: StatusCode,
|
||||
prop_closure: F,
|
||||
) -> Result<(), quick_xml::Error>
|
||||
where
|
||||
F: FnOnce(&mut Writer<W>) -> Result<(), quick_xml::Error>,
|
||||
{
|
||||
writer
|
||||
.create_element("propstat")
|
||||
.write_inner_content(|writer| {
|
||||
writer
|
||||
.create_element("prop")
|
||||
.write_inner_content(prop_closure)?;
|
||||
|
||||
writer
|
||||
.create_element("status")
|
||||
.write_text_content(BytesText::new(&format!("HTTP/1.1 {}", status)))?;
|
||||
Ok(())
|
||||
})?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
// Writes a propstat response into a multistatus
|
||||
// closure hooks into the <prop> element
|
||||
pub fn write_propstat_response<F, W: Write>(
|
||||
writer: &mut Writer<W>,
|
||||
href: &str,
|
||||
status: StatusCode,
|
||||
prop_closure: F,
|
||||
) -> Result<(), quick_xml::Error>
|
||||
where
|
||||
F: FnOnce(&mut Writer<W>) -> Result<(), quick_xml::Error>,
|
||||
{
|
||||
writer
|
||||
.create_element("response")
|
||||
.write_inner_content(|writer| {
|
||||
writer
|
||||
.create_element("href")
|
||||
.write_text_content(BytesText::new(href))?;
|
||||
|
||||
write_propstat_element(writer, status, prop_closure)?;
|
||||
|
||||
Ok(())
|
||||
})?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn generate_multistatus<'a, F, A>(namespaces: A, closure: F) -> Result<String>
|
||||
where
|
||||
F: FnOnce(&mut Writer<&mut Vec<u8>>) -> Result<(), quick_xml::Error>,
|
||||
A: IntoIterator,
|
||||
A::Item: Into<Attribute<'a>>,
|
||||
{
|
||||
let mut output_buffer = Vec::new();
|
||||
let mut writer = Writer::new_with_indent(&mut output_buffer, b' ', 2);
|
||||
writer
|
||||
.create_element("multistatus")
|
||||
.with_attributes(namespaces)
|
||||
.write_inner_content(closure)?;
|
||||
|
||||
Ok(format!(
|
||||
"<?xml version=\"1.0\" encoding=\"utf-8\"?>\n{}",
|
||||
std::str::from_utf8(&output_buffer)?
|
||||
))
|
||||
}
|
||||
Reference in New Issue
Block a user