A little more preparation for new DAV Push spec

This commit is contained in:
Lennart
2025-05-02 22:22:58 +02:00
parent 6330021f05
commit 3170ca1d08
4 changed files with 91 additions and 17 deletions

View File

@@ -1,6 +1,6 @@
use actix_web::{FromRequest, HttpRequest, ResponseError, http::StatusCode};
use futures_util::future::{Ready, err, ok};
use rustical_xml::ValueSerialize;
use rustical_xml::{ValueDeserialize, ValueSerialize, XmlError};
use thiserror::Error;
#[derive(Error, Debug)]
@@ -25,12 +25,25 @@ impl ValueSerialize for Depth {
match self {
Depth::Zero => "0",
Depth::One => "1",
Depth::Infinity => "Infinity",
Depth::Infinity => "infinity",
}
.to_owned()
}
}
impl ValueDeserialize for Depth {
fn deserialize(val: &str) -> Result<Self, XmlError> {
match val {
"0" => Ok(Self::Zero),
"1" => Ok(Self::One),
"infinity" | "Infinity" => Ok(Self::Infinity),
_ => Err(XmlError::InvalidVariant(
"Invalid value for depth".to_owned(),
)),
}
}
}
impl TryFrom<&[u8]> for Depth {
type Error = InvalidDepthHeader;

View File

@@ -1,6 +1,6 @@
use crate::{ContentUpdate, PropertyUpdate, SupportedTrigger, SupportedTriggers, Transports};
use crate::{ContentUpdate, PropertyUpdate, SupportedTriggers, Transports, Trigger};
use rustical_dav::header::Depth;
use rustical_xml::{EnumUnitVariants, EnumVariants, XmlDeserialize, XmlSerialize};
use rustical_xml::{EnumUnitVariants, EnumVariants, Unparsed, XmlDeserialize, XmlSerialize};
#[derive(XmlDeserialize, XmlSerialize, PartialEq, Clone, EnumUnitVariants, EnumVariants)]
#[xml(unit_variants_ident = "DavPushExtensionPropName")]
@@ -21,8 +21,8 @@ pub trait DavPushExtension {
fn supported_triggers(&self) -> SupportedTriggers {
SupportedTriggers(vec![
SupportedTrigger::ContentUpdate(ContentUpdate(Depth::One)),
SupportedTrigger::PropertyUpdate(PropertyUpdate(Depth::One)),
Trigger::ContentUpdate(ContentUpdate(Depth::One)),
Trigger::PropertyUpdate(PropertyUpdate(Depth::One)),
])
}

View File

@@ -1,5 +1,5 @@
use rustical_dav::header::Depth;
use rustical_xml::XmlSerialize;
use rustical_xml::{Unparsed, XmlDeserialize, XmlSerialize};
#[derive(Debug, Clone, XmlSerialize, PartialEq)]
pub enum Transport {
@@ -22,23 +22,39 @@ impl Default for Transports {
}
}
#[derive(XmlSerialize, PartialEq, Clone)]
pub struct SupportedTriggers(#[xml(flatten, ty = "untagged")] pub Vec<SupportedTrigger>);
#[derive(XmlSerialize, XmlDeserialize, PartialEq, Clone)]
pub struct SupportedTriggers(#[xml(flatten, ty = "untagged")] pub Vec<Trigger>);
#[derive(XmlSerialize, PartialEq, Clone)]
pub enum SupportedTrigger {
#[derive(XmlSerialize, XmlDeserialize, PartialEq, Debug, Clone)]
pub enum Trigger {
#[xml(ns = "rustical_dav::namespace::NS_DAVPUSH")]
ContentUpdate(ContentUpdate),
#[xml(ns = "rustical_dav::namespace::NS_DAVPUSH")]
PropertyUpdate(PropertyUpdate),
}
#[derive(XmlSerialize, PartialEq, Clone)]
#[derive(XmlSerialize, XmlDeserialize, PartialEq, Clone, Debug)]
pub struct ContentUpdate(
#[xml(rename = b"depth", ns = "rustical_dav::namespace::NS_DAV")] pub Depth,
);
#[derive(XmlSerialize, PartialEq, Clone)]
#[derive(XmlSerialize, PartialEq, Clone, Debug)]
pub struct PropertyUpdate(
#[xml(rename = b"depth", ns = "rustical_dav::namespace::NS_DAV")] pub Depth,
);
impl XmlDeserialize for PropertyUpdate {
fn deserialize<R: std::io::BufRead>(
reader: &mut quick_xml::NsReader<R>,
start: &quick_xml::events::BytesStart,
empty: bool,
) -> Result<Self, rustical_xml::XmlError> {
#[derive(XmlDeserialize, PartialEq, Clone, Debug)]
struct FakePropertyUpdate(
#[xml(rename = b"depth", ns = "rustical_dav::namespace::NS_DAV")] pub Depth,
#[xml(rename = b"prop", ns = "rustical_dav::namespace::NS_DAV")] pub Unparsed,
);
let FakePropertyUpdate(depth, _) = FakePropertyUpdate::deserialize(reader, start, empty)?;
Ok(Self(depth))
}
}

View File

@@ -1,10 +1,25 @@
use rustical_xml::{XmlDeserialize, XmlRootTag};
use crate::Trigger;
use rustical_xml::{XmlDeserialize, XmlRootTag, XmlSerialize};
#[derive(XmlDeserialize, Clone, Debug, PartialEq)]
#[xml(ns = "crate::namespace::NS_DAVPUSH")]
pub struct WebPushSubscription {
#[xml(ns = "rustical_dav::namespace::NS_DAVPUSH")]
pub push_resource: String,
#[xml(ns = "rustical_dav::namespace::NS_DAVPUSH")]
pub content_encoding: String,
#[xml(ns = "rustical_dav::namespace::NS_DAVPUSH")]
pub subscription_public_key: SubscriptionPublicKey,
#[xml(ns = "rustical_dav::namespace::NS_DAVPUSH")]
pub auth_secret: String,
}
#[derive(XmlDeserialize, Clone, Debug, PartialEq)]
pub struct SubscriptionPublicKey {
#[xml(ty = "attr", rename = b"type")]
ty: String,
#[xml(ty = "text")]
key: String,
}
#[derive(XmlDeserialize, Clone, Debug, PartialEq)]
@@ -13,6 +28,9 @@ pub struct SubscriptionElement {
pub web_push_subscription: WebPushSubscription,
}
#[derive(XmlDeserialize, XmlSerialize, Clone, Debug, PartialEq)]
pub struct TriggerElement(#[xml(ty = "untagged", flatten)] Vec<Trigger>);
#[derive(XmlDeserialize, XmlRootTag, Clone, Debug, PartialEq)]
#[xml(root = b"push-register")]
#[xml(ns = "rustical_dav::namespace::NS_DAVPUSH")]
@@ -21,11 +39,16 @@ pub struct PushRegister {
pub subscription: SubscriptionElement,
#[xml(ns = "rustical_dav::namespace::NS_DAVPUSH")]
pub expires: Option<String>,
#[xml(ns = "rustical_dav::namespace::NS_DAVPUSH")]
pub trigger: TriggerElement,
}
#[cfg(test)]
mod tests {
use crate::{ContentUpdate, PropertyUpdate};
use super::*;
use rustical_dav::header::Depth;
use rustical_xml::XmlDocument;
#[test]
@@ -33,12 +56,27 @@ mod tests {
let push_register = PushRegister::parse_str(
r#"
<?xml version="1.0" encoding="utf-8" ?>
<push-register xmlns="https://bitfire.at/webdav-push">
<push-register xmlns="https://bitfire.at/webdav-push" xmlns:D="DAV:">
<subscription>
<web-push-subscription>
<push-resource>https://up.example.net/yohd4yai5Phiz1wi</push-resource>
<content-encoding>aes128gcm</content-encoding>
<subscription-public-key type="p256dh">BCVxsr7N_eNgVRqvHtD0zTZsEc6-VV-JvLexhqUzORcxaOzi6-AYWXvTBHm4bjyPjs7Vd8pZGH6SRpkNtoIAiw4</subscription-public-key>
<auth-secret>BTBZMqHH6r4Tts7J_aSIgg</auth-secret>
</web-push-subscription>
</subscription>
<trigger>
<content-update>
<D:depth>infinity</D:depth>
</content-update>
<property-update>
<D:depth>0</D:depth>
<D:prop>
<D:displayname/>
<D:owner/>
</D:prop>
</property-update>
</trigger>
<expires>Wed, 20 Dec 2023 10:03:31 GMT</expires>
</push-register>
"#,
@@ -49,10 +87,17 @@ mod tests {
PushRegister {
subscription: SubscriptionElement {
web_push_subscription: WebPushSubscription {
push_resource: "https://up.example.net/yohd4yai5Phiz1wi".to_owned()
push_resource: "https://up.example.net/yohd4yai5Phiz1wi".to_owned(),
content_encoding: "aes128gcm".to_owned(),
subscription_public_key: SubscriptionPublicKey { ty: "p256dh".to_owned(), key: "BCVxsr7N_eNgVRqvHtD0zTZsEc6-VV-JvLexhqUzORcxaOzi6-AYWXvTBHm4bjyPjs7Vd8pZGH6SRpkNtoIAiw4".to_owned() },
auth_secret: "BTBZMqHH6r4Tts7J_aSIgg".to_owned()
}
},
expires: Some("Wed, 20 Dec 2023 10:03:31 GMT".to_owned())
expires: Some("Wed, 20 Dec 2023 10:03:31 GMT".to_owned()),
trigger: TriggerElement(vec![
Trigger::ContentUpdate(ContentUpdate(Depth::Infinity)),
Trigger::PropertyUpdate(PropertyUpdate(Depth::Zero)),
])
}
)
}