some preparations for WebDav Push

This commit is contained in:
Lennart
2024-11-11 19:33:06 +01:00
parent b77a7f2a03
commit 6747fde623
8 changed files with 89 additions and 3 deletions

1
Cargo.lock generated
View File

@@ -2469,6 +2469,7 @@ dependencies = [
"rustical_dav", "rustical_dav",
"rustical_store", "rustical_store",
"serde", "serde",
"sha2",
"strum", "strum",
"thiserror", "thiserror",
"tokio", "tokio",

View File

@@ -25,3 +25,4 @@ url = { workspace = true }
rustical_dav = { workspace = true } rustical_dav = { workspace = true }
rustical_store = { workspace = true } rustical_store = { workspace = true }
chrono = { workspace = true } chrono = { workspace = true }
sha2 = { workspace = true }

View File

@@ -1,2 +1,3 @@
pub mod mkcalendar; pub mod mkcalendar;
pub mod post;
pub mod report; pub mod report;

View File

@@ -0,0 +1,15 @@
use actix_web::{web::Data, web::Path, Responder};
use rustical_store::{auth::User, CalendarStore};
use tracing::instrument;
use tracing_actix_web::RootSpan;
#[instrument(parent = root_span.id(), skip(store, root_span))]
pub async fn route_post<C: CalendarStore + ?Sized>(
path: Path<(String, String)>,
body: String,
user: User,
store: Data<C>,
root_span: RootSpan,
) -> impl Responder {
"asd"
}

View File

@@ -86,3 +86,37 @@ impl Default for SupportedReportSet {
} }
} }
} }
#[derive(Debug, Clone, Serialize, PartialEq)]
#[serde(rename_all = "kebab-case")]
pub enum Transport {
#[serde(rename = "P:web-push")]
WebPush,
}
#[derive(Debug, Clone, Serialize, PartialEq)]
#[serde(rename_all = "kebab-case")]
pub struct TransportWrapper {
#[serde(rename = "$value")]
transport: Transport,
}
#[derive(Debug, Clone, Serialize, PartialEq)]
#[serde(rename_all = "kebab-case")]
pub struct Transports {
// NOTE: Here we implement an older version of the spec since the new property name is not reflected
// in DAVx5 yet
// https://github.com/bitfireAT/webdav-push/commit/461259a2f2174454b2b00033419b11fac52b79e3
#[serde(rename = "P:transport")]
transports: Vec<TransportWrapper>,
}
impl Default for Transports {
fn default() -> Self {
Self {
transports: vec![TransportWrapper {
transport: Transport::WebPush,
}],
}
}
}

View File

@@ -1,8 +1,9 @@
use super::methods::mkcalendar::route_mkcalendar; use super::methods::mkcalendar::route_mkcalendar;
use super::methods::post::route_post;
use super::methods::report::route_report_calendar; use super::methods::report::route_report_calendar;
use super::prop::{ use super::prop::{
SupportedCalendarComponent, SupportedCalendarComponentSet, SupportedCalendarData, SupportedCalendarComponent, SupportedCalendarComponentSet, SupportedCalendarData,
SupportedReportSet, SupportedReportSet, Transports,
}; };
use crate::calendar_object::resource::CalendarObjectResource; use crate::calendar_object::resource::CalendarObjectResource;
use crate::principal::PrincipalResource; use crate::principal::PrincipalResource;
@@ -12,6 +13,8 @@ use actix_web::http::Method;
use actix_web::web; use actix_web::web;
use actix_web::{web::Data, HttpRequest}; use actix_web::{web::Data, HttpRequest};
use async_trait::async_trait; use async_trait::async_trait;
use base64::prelude::{BASE64_STANDARD, BASE64_URL_SAFE};
use base64::Engine;
use derive_more::derive::{From, Into}; use derive_more::derive::{From, Into};
use rustical_dav::privileges::UserPrivilegeSet; use rustical_dav::privileges::UserPrivilegeSet;
use rustical_dav::resource::{Resource, ResourceService}; use rustical_dav::resource::{Resource, ResourceService};
@@ -19,6 +22,7 @@ use rustical_dav::xml::HrefElement;
use rustical_store::auth::User; use rustical_store::auth::User;
use rustical_store::{Calendar, CalendarStore}; use rustical_store::{Calendar, CalendarStore};
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
use sha2::{Digest, Sha256};
use std::str::FromStr; use std::str::FromStr;
use std::sync::Arc; use std::sync::Arc;
use strum::{EnumString, VariantNames}; use strum::{EnumString, VariantNames};
@@ -33,13 +37,15 @@ pub struct CalendarResourceService<C: CalendarStore + ?Sized> {
#[strum(serialize_all = "kebab-case")] #[strum(serialize_all = "kebab-case")]
pub enum CalendarPropName { pub enum CalendarPropName {
Displayname, Displayname,
Getcontenttype,
Transports,
Topic,
CalendarColor, CalendarColor,
CalendarDescription, CalendarDescription,
CalendarTimezone, CalendarTimezone,
CalendarOrder, CalendarOrder,
SupportedCalendarComponentSet, SupportedCalendarComponentSet,
SupportedCalendarData, SupportedCalendarData,
Getcontenttype,
MaxResourceSize, MaxResourceSize,
SupportedReportSet, SupportedReportSet,
SyncToken, SyncToken,
@@ -54,6 +60,15 @@ pub enum CalendarProp {
Displayname(Option<String>), Displayname(Option<String>),
Getcontenttype(String), Getcontenttype(String),
// WebDav Push
// NOTE: Here we implement an older version of the spec since the new property name is not reflected
// in DAVx5 yet
// https://github.com/bitfireAT/webdav-push/commit/461259a2f2174454b2b00033419b11fac52b79e3
#[serde(skip_deserializing)]
#[serde(rename = "P:push-transports", alias = "push-transports")]
Transports(Transports),
Topic(String),
// CalDAV (RFC 4791) // CalDAV (RFC 4791)
#[serde(rename = "IC:calendar-color", alias = "calendar-color")] #[serde(rename = "IC:calendar-color", alias = "calendar-color")]
CalendarColor(Option<String>), CalendarColor(Option<String>),
@@ -111,7 +126,7 @@ impl Resource for CalendarResource {
fn get_prop( fn get_prop(
&self, &self,
_rmap: &ResourceMap, rmap: &ResourceMap,
_user: &User, _user: &User,
prop: &Self::PropName, prop: &Self::PropName,
) -> Result<Self::Prop, Self::Error> { ) -> Result<Self::Prop, Self::Error> {
@@ -146,6 +161,14 @@ impl Resource for CalendarResource {
CalendarPropName::Getcontenttype => { CalendarPropName::Getcontenttype => {
CalendarProp::Getcontenttype("text/calendar;charset=utf-8".to_owned()) CalendarProp::Getcontenttype("text/calendar;charset=utf-8".to_owned())
} }
CalendarPropName::Transports => CalendarProp::Transports(Default::default()),
CalendarPropName::Topic => {
let url = CalendarResource::get_url(rmap, [&self.0.principal, &self.0.id]).unwrap();
let mut hasher = Sha256::new();
hasher.update(url);
let topic = format!("{:x}", hasher.finalize());
CalendarProp::Topic(topic)
}
CalendarPropName::MaxResourceSize => CalendarProp::MaxResourceSize(10000000), CalendarPropName::MaxResourceSize => CalendarProp::MaxResourceSize(10000000),
CalendarPropName::SupportedReportSet => { CalendarPropName::SupportedReportSet => {
CalendarProp::SupportedReportSet(SupportedReportSet::default()) CalendarProp::SupportedReportSet(SupportedReportSet::default())
@@ -185,6 +208,8 @@ impl Resource for CalendarResource {
} }
CalendarProp::SupportedCalendarData(_) => Err(rustical_dav::Error::PropReadOnly), CalendarProp::SupportedCalendarData(_) => Err(rustical_dav::Error::PropReadOnly),
CalendarProp::Getcontenttype(_) => Err(rustical_dav::Error::PropReadOnly), CalendarProp::Getcontenttype(_) => Err(rustical_dav::Error::PropReadOnly),
CalendarProp::Transports(_) => Err(rustical_dav::Error::PropReadOnly),
CalendarProp::Topic(_) => Err(rustical_dav::Error::PropReadOnly),
CalendarProp::MaxResourceSize(_) => Err(rustical_dav::Error::PropReadOnly), CalendarProp::MaxResourceSize(_) => Err(rustical_dav::Error::PropReadOnly),
CalendarProp::SupportedReportSet(_) => Err(rustical_dav::Error::PropReadOnly), CalendarProp::SupportedReportSet(_) => Err(rustical_dav::Error::PropReadOnly),
CalendarProp::SyncToken(_) => Err(rustical_dav::Error::PropReadOnly), CalendarProp::SyncToken(_) => Err(rustical_dav::Error::PropReadOnly),
@@ -222,6 +247,8 @@ impl Resource for CalendarResource {
} }
CalendarPropName::SupportedCalendarData => Err(rustical_dav::Error::PropReadOnly), CalendarPropName::SupportedCalendarData => Err(rustical_dav::Error::PropReadOnly),
CalendarPropName::Getcontenttype => Err(rustical_dav::Error::PropReadOnly), CalendarPropName::Getcontenttype => Err(rustical_dav::Error::PropReadOnly),
CalendarPropName::Transports => Err(rustical_dav::Error::PropReadOnly),
CalendarPropName::Topic => Err(rustical_dav::Error::PropReadOnly),
CalendarPropName::MaxResourceSize => Err(rustical_dav::Error::PropReadOnly), CalendarPropName::MaxResourceSize => Err(rustical_dav::Error::PropReadOnly),
CalendarPropName::SupportedReportSet => Err(rustical_dav::Error::PropReadOnly), CalendarPropName::SupportedReportSet => Err(rustical_dav::Error::PropReadOnly),
CalendarPropName::SyncToken => Err(rustical_dav::Error::PropReadOnly), CalendarPropName::SyncToken => Err(rustical_dav::Error::PropReadOnly),
@@ -329,5 +356,6 @@ impl<C: CalendarStore + ?Sized> ResourceService for CalendarResourceService<C> {
res.route(report_method.to(route_report_calendar::<C>)) res.route(report_method.to(route_report_calendar::<C>))
.route(mkcalendar_method.to(route_mkcalendar::<C>)) .route(mkcalendar_method.to(route_mkcalendar::<C>))
.post(route_post::<C>)
} }
} }

View File

@@ -5,6 +5,7 @@ use quick_xml::events::attributes::Attribute;
// Can also generate appropriate attributes for quick_xml // Can also generate appropriate attributes for quick_xml
pub enum Namespace { pub enum Namespace {
Dav, Dav,
DavPush,
CalDAV, CalDAV,
CardDAV, CardDAV,
ICal, ICal,
@@ -16,6 +17,7 @@ impl Namespace {
pub fn as_str(&self) -> &'static str { pub fn as_str(&self) -> &'static str {
match self { match self {
Self::Dav => "DAV:", Self::Dav => "DAV:",
Self::DavPush => "DAV:Push",
Self::CalDAV => "urn:ietf:params:xml:ns:caldav", Self::CalDAV => "urn:ietf:params:xml:ns:caldav",
Self::CardDAV => "urn:ietf:params:xml:ns:carddav", Self::CardDAV => "urn:ietf:params:xml:ns:carddav",
Self::ICal => "http://apple.com/ns/ical/", Self::ICal => "http://apple.com/ns/ical/",
@@ -28,6 +30,7 @@ impl Namespace {
pub fn xml_attr(&self) -> &'static str { pub fn xml_attr(&self) -> &'static str {
match self { match self {
Self::Dav => "xmlns", Self::Dav => "xmlns",
Self::DavPush => "xmlns:P",
Self::CalDAV => "xmlns:C", Self::CalDAV => "xmlns:C",
Self::CardDAV => "xmlns:CARD", Self::CardDAV => "xmlns:CARD",
Self::ICal => "xmlns:IC", Self::ICal => "xmlns:IC",

View File

@@ -79,6 +79,8 @@ pub struct MultistatusElement<PropType: Serialize, MemberPropType: Serialize> {
pub member_responses: Vec<ResponseElement<MemberPropType>>, pub member_responses: Vec<ResponseElement<MemberPropType>>,
#[serde(rename = "@xmlns")] #[serde(rename = "@xmlns")]
pub ns_dav: &'static str, pub ns_dav: &'static str,
#[serde(rename = "@xmlns:P")]
pub ns_davpush: &'static str,
#[serde(rename = "@xmlns:C")] #[serde(rename = "@xmlns:C")]
pub ns_caldav: &'static str, pub ns_caldav: &'static str,
#[serde(rename = "@xmlns:IC")] #[serde(rename = "@xmlns:IC")]
@@ -97,6 +99,7 @@ impl<T1: Serialize, T2: Serialize> Default for MultistatusElement<T1, T2> {
responses: vec![], responses: vec![],
member_responses: vec![], member_responses: vec![],
ns_dav: Namespace::Dav.as_str(), ns_dav: Namespace::Dav.as_str(),
ns_davpush: Namespace::DavPush.as_str(),
ns_caldav: Namespace::CalDAV.as_str(), ns_caldav: Namespace::CalDAV.as_str(),
ns_ical: Namespace::ICal.as_str(), ns_ical: Namespace::ICal.as_str(),
ns_calendarserver: Namespace::CServer.as_str(), ns_calendarserver: Namespace::CServer.as_str(),