xml: Strict namespace, some tests and restructuring

This commit is contained in:
Lennart
2025-01-15 19:12:54 +01:00
parent d021e7b8bf
commit 3e0571bb72
15 changed files with 176 additions and 157 deletions

View File

@@ -21,6 +21,7 @@ pub(crate) struct CalendarMultigetRequest {
#[xml(ty = "untagged")]
pub(crate) prop: PropfindType,
#[xml(flatten)]
#[xml(ns = "rustical_dav::namespace::NS_DAV")]
pub(crate) href: Vec<String>,
}

View File

@@ -19,15 +19,19 @@ use crate::{
#[allow(dead_code)]
pub(crate) struct TimeRangeElement {
#[xml(ty = "attr")]
#[xml(ns = "rustical_dav::namespace::NS_CALDAV")]
pub(crate) start: Option<UtcDateTime>,
#[xml(ty = "attr")]
#[xml(ns = "rustical_dav::namespace::NS_CALDAV")]
pub(crate) end: Option<UtcDateTime>,
}
#[derive(XmlDeserialize, Clone, Debug, PartialEq)]
#[allow(dead_code)]
struct ParamFilterElement {
#[xml(ns = "rustical_dav::namespace::NS_CALDAV")]
is_not_defined: Option<()>,
#[xml(ns = "rustical_dav::namespace::NS_CALDAV")]
text_match: Option<TextMatchElement>,
#[xml(ty = "attr")]
@@ -38,18 +42,24 @@ struct ParamFilterElement {
#[allow(dead_code)]
struct TextMatchElement {
#[xml(ty = "attr")]
#[xml(ns = "rustical_dav::namespace::NS_CALDAV")]
collation: String,
#[xml(ty = "attr")]
#[xml(ns = "rustical_dav::namespace::NS_CALDAV")]
negate_collation: String,
}
#[derive(XmlDeserialize, Clone, Debug, PartialEq)]
#[allow(dead_code)]
pub(crate) struct PropFilterElement {
#[xml(ns = "rustical_dav::namespace::NS_CALDAV")]
is_not_defined: Option<()>,
#[xml(ns = "rustical_dav::namespace::NS_CALDAV")]
time_range: Option<TimeRangeElement>,
#[xml(ns = "rustical_dav::namespace::NS_CALDAV")]
text_match: Option<TextMatchElement>,
#[xml(flatten)]
#[xml(ns = "rustical_dav::namespace::NS_CALDAV")]
param_filter: Vec<ParamFilterElement>,
}
@@ -57,14 +67,16 @@ pub(crate) struct PropFilterElement {
#[allow(dead_code)]
// https://datatracker.ietf.org/doc/html/rfc4791#section-9.7.1
pub(crate) struct CompFilterElement {
#[xml(ns = "rustical_dav::namespace::NS_CALDAV")]
pub(crate) is_not_defined: Option<()>,
#[xml(ns = "rustical_dav::namespace::NS_CALDAV")]
pub(crate) time_range: Option<TimeRangeElement>,
#[xml(flatten)]
#[xml(ns = "rustical_dav::namespace::NS_CALDAV", flatten)]
pub(crate) prop_filter: Vec<PropFilterElement>,
#[xml(flatten)]
#[xml(ns = "rustical_dav::namespace::NS_CALDAV", flatten)]
pub(crate) comp_filter: Vec<CompFilterElement>,
#[xml(ty = "attr")]
#[xml(ns = "rustical_dav::namespace::NS_CALDAV", ty = "attr")]
pub(crate) name: String,
}
@@ -155,6 +167,7 @@ impl CompFilterElement {
#[allow(dead_code)]
// https://datatracker.ietf.org/doc/html/rfc4791#section-9.7
pub(crate) struct FilterElement {
#[xml(ns = "rustical_dav::namespace::NS_CALDAV")]
pub(crate) comp_filter: CompFilterElement,
}
@@ -170,8 +183,11 @@ impl FilterElement {
pub struct CalendarQueryRequest {
#[xml(ty = "untagged")]
pub prop: PropfindType,
#[xml(ns = "rustical_dav::namespace::NS_CALDAV")]
pub(crate) filter: Option<FilterElement>,
#[xml(ns = "rustical_dav::namespace::NS_CALDAV")]
pub(crate) timezone: Option<String>,
#[xml(ns = "rustical_dav::namespace::NS_CALDAV")]
pub(crate) timezone_id: Option<String>,
}

View File

@@ -5,9 +5,10 @@ use actix_web::{
};
use calendar_multiget::{handle_calendar_multiget, CalendarMultigetRequest};
use calendar_query::{handle_calendar_query, CalendarQueryRequest};
use rustical_dav::xml::sync_collection::SyncCollectionRequest;
use rustical_store::{auth::User, CalendarStore};
use rustical_xml::{XmlDeserialize, XmlDocument};
use sync_collection::{handle_sync_collection, SyncCollectionRequest};
use sync_collection::handle_sync_collection;
use tracing::instrument;
mod calendar_multiget;
@@ -16,8 +17,11 @@ mod sync_collection;
#[derive(XmlDeserialize, XmlDocument, Clone, Debug, PartialEq)]
pub(crate) enum ReportRequest {
#[xml(ns = "rustical_dav::namespace::NS_CALDAV")]
CalendarMultiget(CalendarMultigetRequest),
#[xml(ns = "rustical_dav::namespace::NS_CALDAV")]
CalendarQuery(CalendarQueryRequest),
#[xml(ns = "rustical_dav::namespace::NS_DAV")]
SyncCollection(SyncCollectionRequest),
}
@@ -133,4 +137,31 @@ mod tests {
})
)
}
#[test]
fn test_xml_calendar_multiget() {
let report_request = ReportRequest::parse_str(r#"
<?xml version="1.0" encoding="UTF-8"?>
<calendar-multiget xmlns="urn:ietf:params:xml:ns:caldav" xmlns:D="DAV:">
<D:prop>
<D:getetag/>
<D:displayname/>
</D:prop>
<D:href>/caldav/user/user/6f787542-5256-401a-8db97003260da/ae7a998fdfd1d84a20391168962c62b</D:href>
</calendar-multiget>
"#).unwrap();
assert_eq!(
report_request,
ReportRequest::CalendarMultiget(CalendarMultigetRequest {
prop: rustical_dav::xml::PropfindType::Prop(PropElement(vec![
Propname("getetag".to_owned()),
Propname("displayname".to_owned())
])),
href: vec![
"/caldav/user/user/6f787542-5256-401a-8db97003260da/ae7a998fdfd1d84a20391168962c62b".to_owned()
]
})
)
}
}

View File

@@ -1,65 +1,22 @@
use actix_web::{http::StatusCode, HttpRequest};
use rustical_dav::{
resource::{CommonPropertiesProp, EitherProp, Resource},
xml::{multistatus::ResponseElement, MultistatusElement},
xml::{PropElement, PropfindType},
xml::{
multistatus::ResponseElement, sync_collection::SyncCollectionRequest, MultistatusElement,
PropElement, PropfindType,
},
};
use rustical_store::{
auth::User,
synctoken::{format_synctoken, parse_synctoken},
CalendarStore,
};
use rustical_xml::{ValueDeserialize, ValueSerialize, XmlDeserialize};
use crate::{
calendar_object::resource::{CalendarObjectProp, CalendarObjectResource},
Error,
};
#[derive(Clone, Debug, PartialEq)]
pub(crate) enum SyncLevel {
One,
Infinity,
}
impl ValueDeserialize for SyncLevel {
fn deserialize(val: &str) -> Result<Self, rustical_xml::XmlError> {
Ok(match val {
"1" => Self::One,
"Infinity" => Self::Infinity,
_ => {
return Err(rustical_xml::XmlError::Other(
"Invalid sync-level".to_owned(),
))
}
})
}
}
impl ValueSerialize for SyncLevel {
fn serialize(&self) -> String {
match self {
SyncLevel::One => "1",
SyncLevel::Infinity => "Infinity",
}
.to_owned()
}
}
#[derive(XmlDeserialize, Clone, Debug, PartialEq)]
#[allow(dead_code)]
// <!ELEMENT sync-collection (sync-token, sync-level, limit?, prop)>
// <!-- DAV:limit defined in RFC 5323, Section 5.17 -->
// <!-- DAV:prop defined in RFC 4918, Section 14.18 -->
pub(crate) struct SyncCollectionRequest {
pub(crate) sync_token: String,
pub(crate) sync_level: SyncLevel,
pub(crate) timezone: Option<String>,
#[xml(ty = "untagged")]
pub prop: PropfindType,
pub(crate) limit: Option<u64>,
}
pub async fn handle_sync_collection<C: CalendarStore>(
sync_collection: SyncCollectionRequest,
req: HttpRequest,