Move Resource and xml_snippets to dav crate

This commit is contained in:
Lennart
2023-09-14 13:39:53 +02:00
parent d8e6b0e0e0
commit c8bd214438
11 changed files with 19 additions and 17 deletions

View File

@@ -17,10 +17,8 @@ use tokio::sync::RwLock;
pub mod depth_extractor;
pub mod error;
pub mod proptypes;
pub mod resource;
pub mod resources;
pub mod routes;
mod xml_snippets;
pub struct CalDavContext<C: CalendarStore> {
pub prefix: String,

View File

@@ -1,70 +0,0 @@
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())
}
}

View File

@@ -8,11 +8,8 @@ use rustical_auth::AuthInfo;
use rustical_store::calendar::{Calendar, CalendarStore};
use tokio::sync::RwLock;
use crate::{
proptypes::{write_href_prop, write_string_prop},
resource::Resource,
xml_snippets::write_resourcetype,
};
use crate::proptypes::{write_href_prop, write_string_prop};
use rustical_dav::{resource::Resource, xml_snippets::write_resourcetype};
pub struct CalendarResource<C: CalendarStore> {
pub cal_store: Arc<RwLock<C>>,

View File

@@ -1,11 +1,11 @@
use std::sync::Arc;
use crate::{proptypes::write_string_prop, resource::Resource};
use crate::proptypes::write_string_prop;
use actix_web::{web::Data, HttpRequest};
use anyhow::{anyhow, Result};
use async_trait::async_trait;
use rustical_auth::AuthInfo;
use rustical_dav::resource::Resource;
use rustical_store::calendar::{CalendarStore, Event};
use std::sync::Arc;
use tokio::sync::RwLock;
pub struct EventResource<C: CalendarStore> {

View File

@@ -1,11 +1,12 @@
use std::sync::Arc;
use crate::{proptypes::write_href_prop, resource::Resource, xml_snippets::write_resourcetype};
use crate::proptypes::write_href_prop;
use actix_web::{web::Data, HttpRequest};
use anyhow::{anyhow, Result};
use async_trait::async_trait;
use quick_xml::events::BytesText;
use rustical_auth::AuthInfo;
use rustical_dav::{resource::Resource, xml_snippets::write_resourcetype};
use rustical_store::calendar::CalendarStore;
use tokio::sync::RwLock;

View File

@@ -1,9 +1,9 @@
use crate::{resource::Resource, xml_snippets::write_resourcetype};
use actix_web::HttpRequest;
use anyhow::{anyhow, Result};
use async_trait::async_trait;
use quick_xml::events::BytesText;
use rustical_auth::AuthInfo;
use rustical_dav::{resource::Resource, xml_snippets::write_resourcetype};
pub struct RootResource {
prefix: String,

View File

@@ -1,7 +1,4 @@
use rustical_dav::namespace::Namespace;
use crate::resource::HandlePropfind;
use crate::resources::event::EventResource;
use crate::xml_snippets::generate_multistatus;
use crate::{CalDavContext, Error};
use actix_web::http::header::ContentType;
use actix_web::web::{Data, Path};
@@ -10,6 +7,9 @@ use anyhow::Result;
use quick_xml::events::BytesText;
use roxmltree::{Node, NodeType};
use rustical_auth::{AuthInfoExtractor, CheckAuthentication};
use rustical_dav::namespace::Namespace;
use rustical_dav::resource::HandlePropfind;
use rustical_dav::xml_snippets::generate_multistatus;
use rustical_store::calendar::{Calendar, CalendarStore, Event};
use std::sync::Arc;
use tokio::sync::RwLock;

View File

@@ -1,6 +1,4 @@
use crate::depth_extractor::Depth;
use crate::resource::{HandlePropfind, Resource};
use crate::xml_snippets::generate_multistatus;
use crate::CalDavContext;
use actix_web::http::header::ContentType;
use actix_web::http::StatusCode;
@@ -10,6 +8,8 @@ use anyhow::Result;
use quick_xml::events::BytesText;
use rustical_auth::{AuthInfoExtractor, CheckAuthentication};
use rustical_dav::namespace::Namespace;
use rustical_dav::resource::{HandlePropfind, Resource};
use rustical_dav::xml_snippets::generate_multistatus;
use rustical_store::calendar::CalendarStore;
use thiserror::Error;

View File

@@ -1,109 +0,0 @@
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)?
))
}