WIP: Complete work of propfind parsing

This commit is contained in:
Lennart
2025-06-04 18:11:25 +02:00
parent 5ad6ee2e99
commit e57a14cad1
43 changed files with 875 additions and 1036 deletions

View File

@@ -1,5 +1,4 @@
use super::ReportPropName;
use crate::Error;
use crate::{Error, calendar_object::resource::CalendarObjectPropWrapperName};
use actix_web::dev::{Path, ResourceDef};
use rustical_dav::xml::PropfindType;
use rustical_ical::CalendarObject;
@@ -11,7 +10,7 @@ use rustical_xml::XmlDeserialize;
// <!ELEMENT calendar-query ((DAV:allprop | DAV:propname | DAV:prop)?, href+)>
pub(crate) struct CalendarMultigetRequest {
#[xml(ty = "untagged")]
pub(crate) prop: PropfindType<ReportPropName>,
pub(crate) prop: PropfindType<CalendarObjectPropWrapperName>,
#[xml(flatten)]
#[xml(ns = "rustical_dav::namespace::NS_DAV")]
pub(crate) href: Vec<String>,

View File

@@ -1,5 +1,4 @@
use super::ReportPropName;
use crate::Error;
use crate::{Error, calendar_object::resource::CalendarObjectPropWrapperName};
use rustical_dav::xml::PropfindType;
use rustical_ical::{CalendarObject, UtcDateTime};
use rustical_store::{CalendarStore, calendar_store::CalendarQuery};
@@ -171,7 +170,7 @@ impl From<&FilterElement> for CalendarQuery {
// <!ELEMENT calendar-query ((DAV:allprop | DAV:propname | DAV:prop)?, filter, timezone?)>
pub struct CalendarQueryRequest {
#[xml(ty = "untagged")]
pub prop: PropfindType<ReportPropName>,
pub prop: PropfindType<CalendarObjectPropWrapperName>,
#[xml(ns = "rustical_dav::namespace::NS_CALDAV")]
pub(crate) filter: Option<FilterElement>,
#[xml(ns = "rustical_dav::namespace::NS_CALDAV")]

View File

@@ -1,6 +1,8 @@
use crate::{
CalDavPrincipalUri, Error,
calendar_object::resource::{CalendarObjectPropWrapper, CalendarObjectResource},
calendar_object::resource::{
CalendarObjectPropWrapper, CalendarObjectPropWrapperName, CalendarObjectResource,
},
};
use actix_web::{
HttpRequest, Responder,
@@ -12,11 +14,11 @@ use calendar_query::{CalendarQueryRequest, get_objects_calendar_query};
use rustical_dav::{
resource::{PrincipalUri, Resource},
xml::{
MultistatusElement, PropElement, PropfindType, Propname, multistatus::ResponseElement,
MultistatusElement, PropfindType, multistatus::ResponseElement,
sync_collection::SyncCollectionRequest,
},
};
use rustical_ical::{CalendarObject, UtcDateTime};
use rustical_ical::CalendarObject;
use rustical_store::{CalendarStore, auth::User};
use rustical_xml::{XmlDeserialize, XmlDocument};
use sync_collection::handle_sync_collection;
@@ -26,34 +28,6 @@ mod calendar_multiget;
mod calendar_query;
mod sync_collection;
#[derive(XmlDeserialize, Clone, Debug, PartialEq)]
pub(crate) struct ExpandElement {
#[xml(ty = "attr")]
start: UtcDateTime,
#[xml(ty = "attr")]
end: UtcDateTime,
}
#[derive(XmlDeserialize, Clone, Debug, PartialEq)]
pub struct CalendarData {
#[xml(ns = "rustical_dav::namespace::NS_CALDAV")]
comp: Option<()>,
#[xml(ns = "rustical_dav::namespace::NS_CALDAV")]
expand: Option<ExpandElement>,
#[xml(ns = "rustical_dav::namespace::NS_CALDAV")]
limit_recurrence_set: Option<()>,
#[xml(ns = "rustical_dav::namespace::NS_CALDAV")]
limit_freebusy_set: Option<()>,
}
#[derive(XmlDeserialize, Clone, Debug, PartialEq)]
pub enum ReportPropName {
#[xml(ns = "rustical_dav::namespace::NS_CALDAV")]
CalendarData(CalendarData),
#[xml(other)]
Propname(Propname),
}
#[derive(XmlDeserialize, XmlDocument, Clone, Debug, PartialEq)]
pub(crate) enum ReportRequest {
#[xml(ns = "rustical_dav::namespace::NS_CALDAV")]
@@ -61,31 +35,15 @@ pub(crate) enum ReportRequest {
#[xml(ns = "rustical_dav::namespace::NS_CALDAV")]
CalendarQuery(CalendarQueryRequest),
#[xml(ns = "rustical_dav::namespace::NS_DAV")]
SyncCollection(SyncCollectionRequest<ReportPropName>),
SyncCollection(SyncCollectionRequest<CalendarObjectPropWrapperName>),
}
impl ReportRequest {
fn props(&self) -> Vec<&str> {
let prop_element = match self {
fn props(&self) -> &PropfindType<CalendarObjectPropWrapperName> {
match &self {
ReportRequest::CalendarMultiget(CalendarMultigetRequest { prop, .. }) => prop,
ReportRequest::CalendarQuery(CalendarQueryRequest { prop, .. }) => prop,
ReportRequest::SyncCollection(SyncCollectionRequest { prop, .. }) => prop,
};
match prop_element {
PropfindType::Allprop => {
vec!["allprop"]
}
PropfindType::Propname => {
vec!["propname"]
}
PropfindType::Prop(PropElement(prop_tags)) => prop_tags
.iter()
.map(|propname| match propname {
ReportPropName::Propname(propname) => propname.name.as_str(),
ReportPropName::CalendarData(_) => "calendar-data",
})
.collect(),
}
}
}
@@ -97,7 +55,7 @@ fn objects_response(
principal: &str,
puri: &impl PrincipalUri,
user: &User,
props: &[&str],
prop: &PropfindType<CalendarObjectPropWrapperName>,
) -> Result<MultistatusElement<CalendarObjectPropWrapper, String>, Error> {
let mut responses = Vec::new();
for object in objects {
@@ -107,7 +65,7 @@ fn objects_response(
object,
principal: principal.to_owned(),
}
.propfind(&path, props, puri, user)?,
.propfind_typed(&path, prop, puri, user)?,
);
}
@@ -156,7 +114,7 @@ pub async fn route_report_calendar<C: CalendarStore>(
&principal,
puri.as_ref(),
&user,
&props,
props,
)?
}
ReportRequest::CalendarMultiget(cal_multiget) => {
@@ -175,13 +133,12 @@ pub async fn route_report_calendar<C: CalendarStore>(
&principal,
puri.as_ref(),
&user,
&props,
props,
)?
}
ReportRequest::SyncCollection(sync_collection) => {
handle_sync_collection(
sync_collection,
&props,
req.path(),
puri.as_ref(),
&user,
@@ -197,10 +154,11 @@ pub async fn route_report_calendar<C: CalendarStore>(
#[cfg(test)]
mod tests {
use super::*;
use crate::calendar_object::resource::{CalendarData, CalendarObjectPropName, ExpandElement};
use calendar_query::{CompFilterElement, FilterElement, TimeRangeElement};
use rustical_dav::xml::{PropElement, PropfindType, Propname};
use rustical_dav::xml::PropElement;
use rustical_ical::UtcDateTime;
use rustical_xml::ValueDeserialize;
use rustical_xml::{NamespaceOwned, ValueDeserialize};
#[test]
fn test_xml_calendar_data() {
@@ -222,13 +180,14 @@ mod tests {
report_request,
ReportRequest::CalendarMultiget(CalendarMultigetRequest {
prop: rustical_dav::xml::PropfindType::Prop(PropElement(vec![
ReportPropName::Propname(Propname{name: "getetag".to_owned(), ns: Some("DAV:".into())}),
ReportPropName::Propname(Propname{name: "displayname".to_owned(), ns: Some("DAV:".into())}),
ReportPropName::CalendarData(CalendarData { comp: None, expand: Some(ExpandElement {
CalendarObjectPropWrapperName::CalendarObject(CalendarObjectPropName::Getetag),
CalendarObjectPropWrapperName::CalendarObject(CalendarObjectPropName::CalendarData(
CalendarData { comp: None, expand: Some(ExpandElement {
start: <UtcDateTime as ValueDeserialize>::deserialize("20250426T220000Z").unwrap(),
end: <UtcDateTime as ValueDeserialize>::deserialize("20250503T220000Z").unwrap(),
}), limit_recurrence_set: None, limit_freebusy_set: None })
])),
}), limit_recurrence_set: None, limit_freebusy_set: None }
)),
], vec![(Some(NamespaceOwned(Vec::from("DAV:"))), "displayname".to_string())])),
href: vec![
"/caldav/user/user/6f787542-5256-401a-8db97003260da/ae7a998fdfd1d84a20391168962c62b".to_owned()
]
@@ -258,10 +217,12 @@ mod tests {
assert_eq!(
report_request,
ReportRequest::CalendarQuery(CalendarQueryRequest {
prop: PropfindType::Prop(PropElement(vec![ReportPropName::Propname(Propname {
name: "getetag".to_owned(),
ns: Some("DAV:".into())
})])),
prop: rustical_dav::xml::PropfindType::Prop(PropElement(
vec![CalendarObjectPropWrapperName::CalendarObject(
CalendarObjectPropName::Getetag
),],
vec![]
)),
filter: Some(FilterElement {
comp_filter: CompFilterElement {
is_not_defined: None,
@@ -308,9 +269,8 @@ mod tests {
report_request,
ReportRequest::CalendarMultiget(CalendarMultigetRequest {
prop: rustical_dav::xml::PropfindType::Prop(PropElement(vec![
ReportPropName::Propname(Propname{name: "getetag".to_owned(), ns: Some("DAV:".into())}),
ReportPropName::Propname(Propname{name: "displayname".to_owned(), ns: Some("DAV:".into())})
])),
CalendarObjectPropWrapperName::CalendarObject(CalendarObjectPropName::Getetag),
], vec![(Some(NamespaceOwned(Vec::from("DAV:"))), "displayname".to_string())])),
href: vec![
"/caldav/user/user/6f787542-5256-401a-8db97003260da/ae7a998fdfd1d84a20391168962c62b".to_owned()
]

View File

@@ -1,7 +1,8 @@
use super::ReportPropName;
use crate::{
Error,
calendar_object::resource::{CalendarObjectPropWrapper, CalendarObjectResource},
calendar_object::resource::{
CalendarObjectPropWrapper, CalendarObjectPropWrapperName, CalendarObjectResource,
},
};
use actix_web::http::StatusCode;
use rustical_dav::{
@@ -17,8 +18,7 @@ use rustical_store::{
};
pub async fn handle_sync_collection<C: CalendarStore>(
sync_collection: &SyncCollectionRequest<ReportPropName>,
props: &[&str],
sync_collection: &SyncCollectionRequest<CalendarObjectPropWrapperName>,
path: &str,
puri: &impl PrincipalUri,
user: &User,
@@ -39,7 +39,7 @@ pub async fn handle_sync_collection<C: CalendarStore>(
object,
principal: principal.to_owned(),
}
.propfind(&path, props, puri, user)?,
.propfind_typed(&path, &sync_collection.prop, puri, user)?,
);
}

View File

@@ -19,12 +19,12 @@ use rustical_dav_push::{DavPushExtension, DavPushExtensionProp};
use rustical_ical::CalDateTime;
use rustical_store::auth::User;
use rustical_store::{Calendar, CalendarStore, SubscriptionStore};
use rustical_xml::{EnumUnitVariants, EnumVariants};
use rustical_xml::{EnumVariants, PropName};
use rustical_xml::{XmlDeserialize, XmlSerialize};
use std::str::FromStr;
use std::sync::Arc;
#[derive(XmlDeserialize, XmlSerialize, PartialEq, Clone, EnumVariants, EnumUnitVariants)]
#[derive(XmlDeserialize, XmlSerialize, PartialEq, Clone, EnumVariants, PropName)]
#[xml(unit_variants_ident = "CalendarPropName")]
pub enum CalendarProp {
// WebDAV (RFC 2518)
@@ -64,7 +64,7 @@ pub enum CalendarProp {
MaxDateTime(String),
}
#[derive(XmlDeserialize, XmlSerialize, PartialEq, Clone, EnumVariants, EnumUnitVariants)]
#[derive(XmlDeserialize, XmlSerialize, PartialEq, Clone, EnumVariants, PropName)]
#[xml(unit_variants_ident = "CalendarPropWrapperName", untagged)]
pub enum CalendarPropWrapper {
Calendar(CalendarProp),

View File

@@ -9,9 +9,9 @@ use rustical_dav::{
resource::{PrincipalUri, Resource, ResourceService},
xml::Resourcetype,
};
use rustical_ical::CalendarObject;
use rustical_ical::{CalendarObject, UtcDateTime};
use rustical_store::{CalendarStore, auth::User};
use rustical_xml::{EnumUnitVariants, EnumVariants, XmlDeserialize, XmlSerialize};
use rustical_xml::{EnumVariants, PropName, XmlDeserialize, XmlSerialize};
use serde::Deserialize;
use std::sync::Arc;
@@ -25,7 +25,27 @@ impl<C: CalendarStore> CalendarObjectResourceService<C> {
}
}
#[derive(XmlDeserialize, XmlSerialize, PartialEq, Clone, EnumVariants, EnumUnitVariants)]
#[derive(XmlDeserialize, Clone, Debug, PartialEq, Eq, Hash)]
pub(crate) struct ExpandElement {
#[xml(ty = "attr")]
pub(crate) start: UtcDateTime,
#[xml(ty = "attr")]
pub(crate) end: UtcDateTime,
}
#[derive(XmlDeserialize, Clone, Debug, PartialEq, Default, Eq, Hash)]
pub struct CalendarData {
#[xml(ns = "rustical_dav::namespace::NS_CALDAV")]
pub(crate) comp: Option<()>,
#[xml(ns = "rustical_dav::namespace::NS_CALDAV")]
pub(crate) expand: Option<ExpandElement>,
#[xml(ns = "rustical_dav::namespace::NS_CALDAV")]
pub(crate) limit_recurrence_set: Option<()>,
#[xml(ns = "rustical_dav::namespace::NS_CALDAV")]
pub(crate) limit_freebusy_set: Option<()>,
}
#[derive(XmlDeserialize, XmlSerialize, PartialEq, Clone, EnumVariants, PropName)]
#[xml(unit_variants_ident = "CalendarObjectPropName")]
pub enum CalendarObjectProp {
// WebDAV (RFC 2518)
@@ -36,10 +56,11 @@ pub enum CalendarObjectProp {
// CalDAV (RFC 4791)
#[xml(ns = "rustical_dav::namespace::NS_CALDAV")]
#[xml(prop = "CalendarData")]
CalendarData(String),
}
#[derive(XmlDeserialize, XmlSerialize, PartialEq, Clone, EnumVariants, EnumUnitVariants)]
#[derive(XmlDeserialize, XmlSerialize, PartialEq, Clone, EnumVariants, PropName)]
#[xml(unit_variants_ident = "CalendarObjectPropWrapperName", untagged)]
pub enum CalendarObjectPropWrapper {
CalendarObject(CalendarObjectProp),
@@ -73,8 +94,15 @@ impl Resource for CalendarObjectResource {
CalendarObjectPropName::Getetag => {
CalendarObjectProp::Getetag(self.object.get_etag())
}
CalendarObjectPropName::CalendarData => {
CalendarObjectProp::CalendarData(self.object.get_ics().to_owned())
CalendarObjectPropName::CalendarData(CalendarData { expand, .. }) => {
CalendarObjectProp::CalendarData(if let Some(expand) = expand.as_ref() {
self.object.expand_recurrence(
Some(expand.start.to_utc()),
Some(expand.end.to_utc()),
)?
} else {
self.object.get_ics().to_owned()
})
}
CalendarObjectPropName::Getcontenttype => {
CalendarObjectProp::Getcontenttype("text/calendar;charset=utf-8")

View File

@@ -8,7 +8,7 @@ use rustical_dav::resource::{PrincipalUri, Resource, ResourceService};
use rustical_dav::xml::{Resourcetype, ResourcetypeInner};
use rustical_store::auth::User;
use rustical_store::{CalendarStore, SubscriptionStore};
use rustical_xml::{EnumUnitVariants, EnumVariants, XmlDeserialize, XmlSerialize};
use rustical_xml::{EnumVariants, PropName, XmlDeserialize, XmlSerialize};
use std::sync::Arc;
#[derive(Clone)]
@@ -17,7 +17,7 @@ pub struct CalendarSetResource {
pub(crate) read_only: bool,
}
#[derive(XmlDeserialize, XmlSerialize, PartialEq, Clone, EnumVariants, EnumUnitVariants)]
#[derive(XmlDeserialize, XmlSerialize, PartialEq, Clone, EnumVariants, PropName)]
#[xml(unit_variants_ident = "PrincipalPropWrapperName", untagged)]
pub enum PrincipalPropWrapper {
Common(CommonPropertiesProp),

View File

@@ -9,7 +9,7 @@ use rustical_dav::xml::{HrefElement, Resourcetype, ResourcetypeInner};
use rustical_store::auth::user::PrincipalType;
use rustical_store::auth::{AuthenticationProvider, User};
use rustical_store::{CalendarStore, SubscriptionStore};
use rustical_xml::{EnumUnitVariants, EnumVariants, XmlDeserialize, XmlSerialize};
use rustical_xml::{EnumVariants, PropName, XmlDeserialize, XmlSerialize};
use std::sync::Arc;
#[derive(Clone)]
@@ -21,7 +21,7 @@ pub struct PrincipalResource {
#[derive(XmlDeserialize, XmlSerialize, PartialEq, Clone)]
pub struct CalendarHomeSet(#[xml(ty = "untagged", flatten)] Vec<HrefElement>);
#[derive(XmlDeserialize, XmlSerialize, PartialEq, Clone, EnumVariants, EnumUnitVariants)]
#[derive(XmlDeserialize, XmlSerialize, PartialEq, Clone, EnumVariants, PropName)]
#[xml(unit_variants_ident = "PrincipalPropName")]
pub enum PrincipalProp {
#[xml(ns = "rustical_dav::namespace::NS_DAV")]
@@ -42,7 +42,7 @@ pub enum PrincipalProp {
CalendarHomeSet(CalendarHomeSet),
}
#[derive(XmlDeserialize, XmlSerialize, PartialEq, Clone, EnumVariants, EnumUnitVariants)]
#[derive(XmlDeserialize, XmlSerialize, PartialEq, Clone, EnumVariants, PropName)]
#[xml(unit_variants_ident = "PrincipalPropWrapperName", untagged)]
pub enum PrincipalPropWrapper {
Principal(PrincipalProp),