basic propfind

This commit is contained in:
Quentin 2024-03-17 10:31:05 +01:00
parent 902d33c434
commit f372a95b01
Signed by: quentin
GPG key ID: E9602264D639FF68
5 changed files with 153 additions and 88 deletions

View file

@ -108,13 +108,13 @@ pub struct CalendarMultiget<E: dav::Extension> {
pub struct FreeBusyQuery(pub TimeRange); pub struct FreeBusyQuery(pub TimeRange);
// ----- Hooks ----- // ----- Hooks -----
#[derive(Debug, PartialEq)] #[derive(Debug, PartialEq, Clone)]
pub enum ResourceType { pub enum ResourceType {
Calendar, Calendar,
} }
/// Check the matching Property object for documentation /// Check the matching Property object for documentation
#[derive(Debug, PartialEq)] #[derive(Debug, PartialEq, Clone)]
pub enum PropertyRequest { pub enum PropertyRequest {
CalendarDescription, CalendarDescription,
CalendarTimezone, CalendarTimezone,
@ -129,7 +129,7 @@ pub enum PropertyRequest {
CalendarData(CalendarDataRequest), CalendarData(CalendarDataRequest),
} }
#[derive(Debug, PartialEq)] #[derive(Debug, PartialEq, Clone)]
pub enum Property { pub enum Property {
/// Name: calendar-description /// Name: calendar-description
/// ///
@ -609,7 +609,7 @@ pub enum Property {
CalendarData(CalendarDataPayload), CalendarData(CalendarDataPayload),
} }
#[derive(Debug, PartialEq)] #[derive(Debug, PartialEq, Clone)]
pub enum Violation { pub enum Violation {
/// (DAV:resource-must-be-null): A resource MUST NOT exist at the /// (DAV:resource-must-be-null): A resource MUST NOT exist at the
/// Request-URI; /// Request-URI;
@ -780,7 +780,7 @@ pub enum Violation {
/// If the client chooses a collation not supported by the server, the /// If the client chooses a collation not supported by the server, the
/// server MUST respond with a CALDAV:supported-collation precondition /// server MUST respond with a CALDAV:supported-collation precondition
/// error response. /// error response.
#[derive(Debug, PartialEq)] #[derive(Debug, PartialEq, Clone)]
pub struct SupportedCollation(pub Collation); pub struct SupportedCollation(pub Collation);
/// <!ELEMENT calendar-data (#PCDATA)> /// <!ELEMENT calendar-data (#PCDATA)>
@ -789,7 +789,7 @@ pub struct SupportedCollation(pub Collation);
/// when nested in the DAV:prop XML element in a calendaring /// when nested in the DAV:prop XML element in a calendaring
/// REPORT response to specify the content of a returned /// REPORT response to specify the content of a returned
/// calendar object resource. /// calendar object resource.
#[derive(Debug, PartialEq)] #[derive(Debug, PartialEq, Clone)]
pub struct CalendarDataPayload { pub struct CalendarDataPayload {
pub mime: Option<CalendarDataSupport>, pub mime: Option<CalendarDataSupport>,
pub payload: String, pub payload: String,
@ -802,7 +802,7 @@ pub struct CalendarDataPayload {
/// when nested in the DAV:prop XML element in a calendaring /// when nested in the DAV:prop XML element in a calendaring
/// REPORT request to specify which parts of calendar object /// REPORT request to specify which parts of calendar object
/// resources should be returned in the response; /// resources should be returned in the response;
#[derive(Debug, PartialEq)] #[derive(Debug, PartialEq, Clone)]
pub struct CalendarDataRequest { pub struct CalendarDataRequest {
pub mime: Option<CalendarDataSupport>, pub mime: Option<CalendarDataSupport>,
pub comp: Option<Comp>, pub comp: Option<Comp>,
@ -817,7 +817,7 @@ pub struct CalendarDataRequest {
/// when nested in the CALDAV:supported-calendar-data property /// when nested in the CALDAV:supported-calendar-data property
/// to specify a supported media type for calendar object /// to specify a supported media type for calendar object
/// resources; /// resources;
#[derive(Debug, PartialEq)] #[derive(Debug, PartialEq, Clone)]
pub struct CalendarDataEmpty(pub Option<CalendarDataSupport>); pub struct CalendarDataEmpty(pub Option<CalendarDataSupport>);
/// <!ATTLIST calendar-data content-type CDATA "text/calendar" /// <!ATTLIST calendar-data content-type CDATA "text/calendar"
@ -826,7 +826,7 @@ pub struct CalendarDataEmpty(pub Option<CalendarDataSupport>);
/// version value: a version string /// version value: a version string
/// attributes can be used on all three variants of the /// attributes can be used on all three variants of the
/// CALDAV:calendar-data XML element. /// CALDAV:calendar-data XML element.
#[derive(Debug, PartialEq)] #[derive(Debug, PartialEq, Clone)]
pub struct CalendarDataSupport { pub struct CalendarDataSupport {
pub content_type: String, pub content_type: String,
pub version: String, pub version: String,
@ -852,7 +852,7 @@ pub struct CalendarDataSupport {
/// However, the CALDAV:prop and CALDAV:allprop elements are defined /// However, the CALDAV:prop and CALDAV:allprop elements are defined
/// in the "urn:ietf:params:xml:ns:caldav" namespace instead of the /// in the "urn:ietf:params:xml:ns:caldav" namespace instead of the
/// "DAV:" namespace. /// "DAV:" namespace.
#[derive(Debug, PartialEq)] #[derive(Debug, PartialEq, Clone)]
pub struct Comp { pub struct Comp {
pub name: Component, pub name: Component,
pub prop_kind: Option<PropKind>, pub prop_kind: Option<PropKind>,
@ -872,7 +872,7 @@ pub struct Comp {
/// <C:comp name="VEVENT"/> /// <C:comp name="VEVENT"/>
/// <C:comp name="VTODO"/> /// <C:comp name="VTODO"/>
/// </C:supported-calendar-component-set> /// </C:supported-calendar-component-set>
#[derive(Debug, PartialEq)] #[derive(Debug, PartialEq, Clone)]
pub struct CompSupport(pub Component); pub struct CompSupport(pub Component);
/// Name: allcomp /// Name: allcomp
@ -888,7 +888,7 @@ pub struct CompSupport(pub Component);
/// Definition: /// Definition:
/// ///
/// <!ELEMENT allcomp EMPTY> /// <!ELEMENT allcomp EMPTY>
#[derive(Debug, PartialEq)] #[derive(Debug, PartialEq, Clone)]
pub enum CompKind { pub enum CompKind {
AllComp, AllComp,
Comp(Vec<Comp>), Comp(Vec<Comp>),
@ -912,7 +912,7 @@ pub enum CompKind {
/// allprop element defined in [RFC2518]. However, the CALDAV:allprop /// allprop element defined in [RFC2518]. However, the CALDAV:allprop
/// element is defined in the "urn:ietf:params:xml:ns:caldav" /// element is defined in the "urn:ietf:params:xml:ns:caldav"
/// namespace instead of the "DAV:" namespace. /// namespace instead of the "DAV:" namespace.
#[derive(Debug, PartialEq)] #[derive(Debug, PartialEq, Clone)]
pub enum PropKind { pub enum PropKind {
AllProp, AllProp,
Prop(Vec<CalProp>), Prop(Vec<CalProp>),
@ -942,13 +942,13 @@ pub enum PropKind {
/// element defined in [RFC2518]. However, the CALDAV:prop element is /// element defined in [RFC2518]. However, the CALDAV:prop element is
/// defined in the "urn:ietf:params:xml:ns:caldav" namespace instead /// defined in the "urn:ietf:params:xml:ns:caldav" namespace instead
/// of the "DAV:" namespace. /// of the "DAV:" namespace.
#[derive(Debug, PartialEq)] #[derive(Debug, PartialEq, Clone)]
pub struct CalProp { pub struct CalProp {
pub name: ComponentProperty, pub name: ComponentProperty,
pub novalue: Option<bool>, pub novalue: Option<bool>,
} }
#[derive(Debug, PartialEq)] #[derive(Debug, PartialEq, Clone)]
pub enum RecurrenceModifier { pub enum RecurrenceModifier {
Expand(Expand), Expand(Expand),
LimitRecurrenceSet(LimitRecurrenceSet), LimitRecurrenceSet(LimitRecurrenceSet),
@ -994,7 +994,7 @@ pub enum RecurrenceModifier {
/// end CDATA #REQUIRED> /// end CDATA #REQUIRED>
/// start value: an iCalendar "date with UTC time" /// start value: an iCalendar "date with UTC time"
/// end value: an iCalendar "date with UTC time" /// end value: an iCalendar "date with UTC time"
#[derive(Debug, PartialEq)] #[derive(Debug, PartialEq, Clone)]
pub struct Expand(pub DateTime<Utc>, pub DateTime<Utc>); pub struct Expand(pub DateTime<Utc>, pub DateTime<Utc>);
/// CALDAV:limit-recurrence-set XML Element /// CALDAV:limit-recurrence-set XML Element
@ -1042,7 +1042,7 @@ pub struct Expand(pub DateTime<Utc>, pub DateTime<Utc>);
/// end CDATA #REQUIRED> /// end CDATA #REQUIRED>
/// start value: an iCalendar "date with UTC time" /// start value: an iCalendar "date with UTC time"
/// end value: an iCalendar "date with UTC time" /// end value: an iCalendar "date with UTC time"
#[derive(Debug, PartialEq)] #[derive(Debug, PartialEq, Clone)]
pub struct LimitRecurrenceSet(pub DateTime<Utc>, pub DateTime<Utc>); pub struct LimitRecurrenceSet(pub DateTime<Utc>, pub DateTime<Utc>);
/// Name: limit-freebusy-set /// Name: limit-freebusy-set
@ -1073,11 +1073,11 @@ pub struct LimitRecurrenceSet(pub DateTime<Utc>, pub DateTime<Utc>);
/// end CDATA #REQUIRED> /// end CDATA #REQUIRED>
/// start value: an iCalendar "date with UTC time" /// start value: an iCalendar "date with UTC time"
/// end value: an iCalendar "date with UTC time" /// end value: an iCalendar "date with UTC time"
#[derive(Debug, PartialEq)] #[derive(Debug, PartialEq, Clone)]
pub struct LimitFreebusySet(pub DateTime<Utc>, pub DateTime<Utc>); pub struct LimitFreebusySet(pub DateTime<Utc>, pub DateTime<Utc>);
/// Used by CalendarQuery & CalendarMultiget /// Used by CalendarQuery & CalendarMultiget
#[derive(Debug, PartialEq)] #[derive(Debug, PartialEq, Clone)]
pub enum CalendarSelector<E: dav::Extension> { pub enum CalendarSelector<E: dav::Extension> {
AllProp, AllProp,
PropName, PropName,
@ -1135,20 +1135,20 @@ pub enum CalendarSelector<E: dav::Extension> {
/// name value: a calendar object or calendar component /// name value: a calendar object or calendar component
/// type (e.g., VEVENT) /// type (e.g., VEVENT)
/// ``` /// ```
#[derive(Debug, PartialEq)] #[derive(Debug, PartialEq, Clone)]
pub struct CompFilter { pub struct CompFilter {
pub name: Component, pub name: Component,
// Option 1 = None, Option 2, 3, 4 = Some // Option 1 = None, Option 2, 3, 4 = Some
pub additional_rules: Option<CompFilterRules>, pub additional_rules: Option<CompFilterRules>,
} }
#[derive(Debug, PartialEq)] #[derive(Debug, PartialEq, Clone)]
pub enum CompFilterRules { pub enum CompFilterRules {
// Option 2 // Option 2
IsNotDefined, IsNotDefined,
// Options 3 & 4 // Options 3 & 4
Matches(CompFilterMatch), Matches(CompFilterMatch),
} }
#[derive(Debug, PartialEq)] #[derive(Debug, PartialEq, Clone)]
pub struct CompFilterMatch { pub struct CompFilterMatch {
pub time_range: Option<TimeRange>, pub time_range: Option<TimeRange>,
pub prop_filter: Vec<PropFilter>, pub prop_filter: Vec<PropFilter>,
@ -1201,26 +1201,26 @@ pub struct CompFilterMatch {
/// <!ATTLIST prop-filter name CDATA #REQUIRED> /// <!ATTLIST prop-filter name CDATA #REQUIRED>
/// name value: a calendar property name (e.g., ATTENDEE) /// name value: a calendar property name (e.g., ATTENDEE)
/// ``` /// ```
#[derive(Debug, PartialEq)] #[derive(Debug, PartialEq, Clone)]
pub struct PropFilter { pub struct PropFilter {
pub name: ComponentProperty, pub name: ComponentProperty,
// None = Option 1, Some() = Option 2, 3 & 4 // None = Option 1, Some() = Option 2, 3 & 4
pub additional_rules: Option<PropFilterRules>, pub additional_rules: Option<PropFilterRules>,
} }
#[derive(Debug, PartialEq)] #[derive(Debug, PartialEq, Clone)]
pub enum PropFilterRules { pub enum PropFilterRules {
// Option 2 // Option 2
IsNotDefined, IsNotDefined,
// Options 3 & 4 // Options 3 & 4
Match(PropFilterMatch), Match(PropFilterMatch),
} }
#[derive(Debug, PartialEq)] #[derive(Debug, PartialEq, Clone)]
pub struct PropFilterMatch { pub struct PropFilterMatch {
pub time_range: Option<TimeRange>, pub time_range: Option<TimeRange>,
pub time_or_text: Option<TimeOrText>, pub time_or_text: Option<TimeOrText>,
pub param_filter: Vec<ParamFilter>, pub param_filter: Vec<ParamFilter>,
} }
#[derive(Debug, PartialEq)] #[derive(Debug, PartialEq, Clone)]
pub enum TimeOrText { pub enum TimeOrText {
Time(TimeRange), Time(TimeRange),
Text(TextMatch), Text(TextMatch),
@ -1254,7 +1254,7 @@ pub enum TimeOrText {
/// PCDATA value: string /// PCDATA value: string
/// <!ATTLIST text-match collation CDATA "i;ascii-casemap" /// <!ATTLIST text-match collation CDATA "i;ascii-casemap"
/// negate-condition (yes | no) "no"> /// negate-condition (yes | no) "no">
#[derive(Debug, PartialEq)] #[derive(Debug, PartialEq, Clone)]
pub struct TextMatch { pub struct TextMatch {
pub collation: Option<Collation>, pub collation: Option<Collation>,
pub negate_condition: Option<bool>, pub negate_condition: Option<bool>,
@ -1292,12 +1292,12 @@ pub struct TextMatch {
/// <!ATTLIST param-filter name CDATA #REQUIRED> /// <!ATTLIST param-filter name CDATA #REQUIRED>
/// name value: a property parameter name (e.g., PARTSTAT) /// name value: a property parameter name (e.g., PARTSTAT)
/// ``` /// ```
#[derive(Debug, PartialEq)] #[derive(Debug, PartialEq, Clone)]
pub struct ParamFilter { pub struct ParamFilter {
pub name: PropertyParameter, pub name: PropertyParameter,
pub additional_rules: Option<ParamFilterMatch>, pub additional_rules: Option<ParamFilterMatch>,
} }
#[derive(Debug, PartialEq)] #[derive(Debug, PartialEq, Clone)]
pub enum ParamFilterMatch { pub enum ParamFilterMatch {
IsNotDefined, IsNotDefined,
Match(TextMatch), Match(TextMatch),
@ -1353,7 +1353,7 @@ pub enum ParamFilterMatch {
/// ///
/// <!ELEMENT timezone (#PCDATA)> /// <!ELEMENT timezone (#PCDATA)>
/// PCDATA value: an iCalendar object with exactly one VTIMEZONE /// PCDATA value: an iCalendar object with exactly one VTIMEZONE
#[derive(Debug, PartialEq)] #[derive(Debug, PartialEq, Clone)]
pub struct TimeZone(pub String); pub struct TimeZone(pub String);
/// Name: filter /// Name: filter
@ -1369,7 +1369,7 @@ pub struct TimeZone(pub String);
/// ///
/// Definition: /// Definition:
/// <!ELEMENT filter (comp-filter)> /// <!ELEMENT filter (comp-filter)>
#[derive(Debug, PartialEq)] #[derive(Debug, PartialEq, Clone)]
pub struct Filter(pub CompFilter); pub struct Filter(pub CompFilter);
/// Name: time-range /// Name: time-range
@ -1381,7 +1381,7 @@ pub struct Filter(pub CompFilter);
/// end CDATA #IMPLIED> /// end CDATA #IMPLIED>
/// start value: an iCalendar "date with UTC time" /// start value: an iCalendar "date with UTC time"
/// end value: an iCalendar "date with UTC time" /// end value: an iCalendar "date with UTC time"
#[derive(Debug, PartialEq)] #[derive(Debug, PartialEq, Clone)]
pub enum TimeRange { pub enum TimeRange {
OnlyStart(DateTime<Utc>), OnlyStart(DateTime<Utc>),
OnlyEnd(DateTime<Utc>), OnlyEnd(DateTime<Utc>),
@ -1391,7 +1391,7 @@ pub enum TimeRange {
// ----------------------- ENUM ATTRIBUTES --------------------- // ----------------------- ENUM ATTRIBUTES ---------------------
/// Known components /// Known components
#[derive(Debug, PartialEq)] #[derive(Debug, PartialEq, Clone)]
pub enum Component { pub enum Component {
VCalendar, VCalendar,
VJournal, VJournal,
@ -1432,11 +1432,11 @@ impl Component {
/// name="VERSION", name="SUMMARY", etc. /// name="VERSION", name="SUMMARY", etc.
/// Can be set on different objects: VCalendar, VEvent, etc. /// Can be set on different objects: VCalendar, VEvent, etc.
/// Might be replaced by an enum later /// Might be replaced by an enum later
#[derive(Debug, PartialEq)] #[derive(Debug, PartialEq, Clone)]
pub struct ComponentProperty(pub String); pub struct ComponentProperty(pub String);
/// like PARSTAT /// like PARSTAT
#[derive(Debug, PartialEq)] #[derive(Debug, PartialEq, Clone)]
pub struct PropertyParameter(pub String); pub struct PropertyParameter(pub String);
impl PropertyParameter { impl PropertyParameter {
pub fn as_str<'a>(&'a self) -> &'a str { pub fn as_str<'a>(&'a self) -> &'a str {
@ -1444,7 +1444,7 @@ impl PropertyParameter {
} }
} }
#[derive(Default,Debug,PartialEq)] #[derive(Default,Debug,PartialEq,Clone)]
pub enum Collation { pub enum Collation {
#[default] #[default]
AsciiCaseMap, AsciiCaseMap,

View file

@ -3,7 +3,7 @@ use super::caltypes as cal;
use super::xml; use super::xml;
use super::error; use super::error;
#[derive(Debug, PartialEq)] #[derive(Debug, PartialEq, Clone)]
pub struct Disabled(()); pub struct Disabled(());
impl xml::QRead<Disabled> for Disabled { impl xml::QRead<Disabled> for Disabled {
async fn qread(_xml: &mut xml::Reader<impl xml::IRead>) -> Result<Self, error::ParsingError> { async fn qread(_xml: &mut xml::Reader<impl xml::IRead>) -> Result<Self, error::ParsingError> {
@ -20,7 +20,7 @@ impl xml::QWrite for Disabled {
/// ///
/// Any extension is kooh is disabled through an object we can't build /// Any extension is kooh is disabled through an object we can't build
/// due to a private inner element. /// due to a private inner element.
#[derive(Debug, PartialEq)] #[derive(Debug, PartialEq, Clone)]
pub struct Core {} pub struct Core {}
impl dav::Extension for Core { impl dav::Extension for Core {
type Error = Disabled; type Error = Disabled;
@ -30,7 +30,7 @@ impl dav::Extension for Core {
} }
// WebDAV with the base Calendar implementation (RFC4791) // WebDAV with the base Calendar implementation (RFC4791)
#[derive(Debug, PartialEq)] #[derive(Debug, PartialEq, Clone)]
pub struct Calendar {} pub struct Calendar {}
impl dav::Extension for Calendar impl dav::Extension for Calendar
{ {

View file

@ -6,7 +6,7 @@ use super::xml;
/// It's how we implement a DAV extension /// It's how we implement a DAV extension
/// (That's the dark magic part...) /// (That's the dark magic part...)
pub trait Extension: std::fmt::Debug + PartialEq { pub trait Extension: std::fmt::Debug + PartialEq + Clone {
type Error: xml::Node<Self::Error>; type Error: xml::Node<Self::Error>;
type Property: xml::Node<Self::Property>; type Property: xml::Node<Self::Property>;
type PropertyRequest: xml::Node<Self::PropertyRequest>; type PropertyRequest: xml::Node<Self::PropertyRequest>;
@ -20,7 +20,7 @@ pub trait Extension: std::fmt::Debug + PartialEq {
/// Purpose: Describes a lock on a resource. /// Purpose: Describes a lock on a resource.
/// <!ELEMENT activelock (lockscope, locktype, depth, owner?, timeout?, /// <!ELEMENT activelock (lockscope, locktype, depth, owner?, timeout?,
/// locktoken?, lockroot)> /// locktoken?, lockroot)>
#[derive(Debug, PartialEq)] #[derive(Debug, PartialEq, Clone)]
pub struct ActiveLock { pub struct ActiveLock {
pub lockscope: LockScope, pub lockscope: LockScope,
pub locktype: LockType, pub locktype: LockType,
@ -54,7 +54,7 @@ pub struct Collection{}
/// Value: "0" | "1" | "infinity" /// Value: "0" | "1" | "infinity"
/// ///
/// <!ELEMENT depth (#PCDATA) > /// <!ELEMENT depth (#PCDATA) >
#[derive(Debug, PartialEq)] #[derive(Debug, PartialEq, Clone)]
pub enum Depth { pub enum Depth {
Zero, Zero,
One, One,
@ -77,9 +77,9 @@ pub enum Depth {
/// postcondition code. Unrecognized elements MUST be ignored. /// postcondition code. Unrecognized elements MUST be ignored.
/// ///
/// <!ELEMENT error ANY > /// <!ELEMENT error ANY >
#[derive(Debug, PartialEq)] #[derive(Debug, PartialEq, Clone)]
pub struct Error<E: Extension>(pub Vec<Violation<E>>); pub struct Error<E: Extension>(pub Vec<Violation<E>>);
#[derive(Debug, PartialEq)] #[derive(Debug, PartialEq, Clone)]
pub enum Violation<E: Extension> { pub enum Violation<E: Extension> {
/// Name: lock-token-matches-request-uri /// Name: lock-token-matches-request-uri
/// ///
@ -190,7 +190,7 @@ pub struct Exclusive {}
/// Value: Simple-ref /// Value: Simple-ref
/// ///
/// <!ELEMENT href (#PCDATA)> /// <!ELEMENT href (#PCDATA)>
#[derive(Debug, PartialEq)] #[derive(Debug, PartialEq, Clone)]
pub struct Href(pub String); pub struct Href(pub String);
@ -206,7 +206,7 @@ pub struct Href(pub String);
/// standards. This element MUST NOT contain text or mixed content. /// standards. This element MUST NOT contain text or mixed content.
/// ///
/// <!ELEMENT include ANY > /// <!ELEMENT include ANY >
#[derive(Debug, PartialEq)] #[derive(Debug, PartialEq, Clone)]
pub struct Include<E: Extension>(pub Vec<PropertyRequest<E>>); pub struct Include<E: Extension>(pub Vec<PropertyRequest<E>>);
/// 14.9. location XML Element /// 14.9. location XML Element
@ -223,7 +223,7 @@ pub struct Include<E: Extension>(pub Vec<PropertyRequest<E>>);
/// that would be used in a Location header. /// that would be used in a Location header.
/// ///
/// <!ELEMENT location (href)> /// <!ELEMENT location (href)>
#[derive(Debug, PartialEq)] #[derive(Debug, PartialEq, Clone)]
pub struct Location(pub Href); pub struct Location(pub Href);
/// 14.10. lockentry XML Element /// 14.10. lockentry XML Element
@ -234,7 +234,7 @@ pub struct Location(pub Href);
/// resource. /// resource.
/// ///
/// <!ELEMENT lockentry (lockscope, locktype) > /// <!ELEMENT lockentry (lockscope, locktype) >
#[derive(Debug, PartialEq)] #[derive(Debug, PartialEq, Clone)]
pub struct LockEntry { pub struct LockEntry {
pub lockscope: LockScope, pub lockscope: LockScope,
pub locktype: LockType, pub locktype: LockType,
@ -248,7 +248,7 @@ pub struct LockEntry {
/// specify the type of lock the client wishes to have created. /// specify the type of lock the client wishes to have created.
/// ///
/// <!ELEMENT lockinfo (lockscope, locktype, owner?) > /// <!ELEMENT lockinfo (lockscope, locktype, owner?) >
#[derive(Debug, PartialEq)] #[derive(Debug, PartialEq, Clone)]
pub struct LockInfo { pub struct LockInfo {
pub lockscope: LockScope, pub lockscope: LockScope,
pub locktype: LockType, pub locktype: LockType,
@ -267,7 +267,7 @@ pub struct LockInfo {
/// values and the response to LOCK requests. /// values and the response to LOCK requests.
/// ///
/// <!ELEMENT lockroot (href) > /// <!ELEMENT lockroot (href) >
#[derive(Debug, PartialEq)] #[derive(Debug, PartialEq, Clone)]
pub struct LockRoot(pub Href); pub struct LockRoot(pub Href);
/// 14.13. lockscope XML Element /// 14.13. lockscope XML Element
@ -277,7 +277,7 @@ pub struct LockRoot(pub Href);
/// Purpose: Specifies whether a lock is an exclusive lock, or a shared /// Purpose: Specifies whether a lock is an exclusive lock, or a shared
/// lock. /// lock.
/// <!ELEMENT lockscope (exclusive | shared) > /// <!ELEMENT lockscope (exclusive | shared) >
#[derive(Debug, PartialEq)] #[derive(Debug, PartialEq, Clone)]
pub enum LockScope { pub enum LockScope {
Exclusive, Exclusive,
Shared Shared
@ -293,7 +293,7 @@ pub enum LockScope {
/// refers to the lock. /// refers to the lock.
/// ///
/// <!ELEMENT locktoken (href) > /// <!ELEMENT locktoken (href) >
#[derive(Debug, PartialEq)] #[derive(Debug, PartialEq, Clone)]
pub struct LockToken(pub Href); pub struct LockToken(pub Href);
/// 14.15. locktype XML Element /// 14.15. locktype XML Element
@ -304,7 +304,7 @@ pub struct LockToken(pub Href);
/// specification only defines one lock type, the write lock. /// specification only defines one lock type, the write lock.
/// ///
/// <!ELEMENT locktype (write) > /// <!ELEMENT locktype (write) >
#[derive(Debug, PartialEq)] #[derive(Debug, PartialEq, Clone)]
pub enum LockType { pub enum LockType {
/// 14.30. write XML Element /// 14.30. write XML Element
/// ///
@ -330,7 +330,7 @@ pub enum LockType {
/// response descriptions contained within the responses. /// response descriptions contained within the responses.
/// ///
/// <!ELEMENT multistatus (response*, responsedescription?) > /// <!ELEMENT multistatus (response*, responsedescription?) >
#[derive(Debug, PartialEq)] #[derive(Debug, PartialEq, Clone)]
pub struct Multistatus<E: Extension, N: xml::Node<N>> { pub struct Multistatus<E: Extension, N: xml::Node<N>> {
pub responses: Vec<Response<E, N>>, pub responses: Vec<Response<E, N>>,
pub responsedescription: Option<ResponseDescription>, pub responsedescription: Option<ResponseDescription>,
@ -360,7 +360,7 @@ pub struct Multistatus<E: Extension, N: xml::Node<N>> {
/// ///
/// <!ELEMENT owner ANY > /// <!ELEMENT owner ANY >
//@FIXME might need support for an extension //@FIXME might need support for an extension
#[derive(Debug, PartialEq)] #[derive(Debug, PartialEq, Clone)]
pub enum Owner { pub enum Owner {
Txt(String), Txt(String),
Href(Href), Href(Href),
@ -381,10 +381,10 @@ pub enum Owner {
/// text or mixed content. /// text or mixed content.
/// ///
/// <!ELEMENT prop ANY > /// <!ELEMENT prop ANY >
#[derive(Debug, PartialEq)] #[derive(Debug, PartialEq, Clone)]
pub struct PropName<E: Extension>(pub Vec<PropertyRequest<E>>); pub struct PropName<E: Extension>(pub Vec<PropertyRequest<E>>);
#[derive(Debug, PartialEq)] #[derive(Debug, PartialEq, Clone)]
pub struct PropValue<E: Extension>(pub Vec<Property<E>>); pub struct PropValue<E: Extension>(pub Vec<Property<E>>);
/// 14.19. propertyupdate XML Element /// 14.19. propertyupdate XML Element
@ -397,10 +397,10 @@ pub struct PropValue<E: Extension>(pub Vec<Property<E>>);
/// required to modify the properties on the resource. /// required to modify the properties on the resource.
/// ///
/// <!ELEMENT propertyupdate (remove | set)+ > /// <!ELEMENT propertyupdate (remove | set)+ >
#[derive(Debug, PartialEq)] #[derive(Debug, PartialEq, Clone)]
pub struct PropertyUpdate<E: Extension>(pub Vec<PropertyUpdateItem<E>>); pub struct PropertyUpdate<E: Extension>(pub Vec<PropertyUpdateItem<E>>);
#[derive(Debug, PartialEq)] #[derive(Debug, PartialEq, Clone)]
pub enum PropertyUpdateItem<E: Extension> { pub enum PropertyUpdateItem<E: Extension> {
Remove(Remove<E>), Remove(Remove<E>),
Set(Set<E>), Set(Set<E>),
@ -440,7 +440,7 @@ pub enum PropertyUpdateItem<E: Extension> {
/// values. /// values.
/// ///
/// <!ELEMENT propfind ( propname | (allprop, include?) | prop ) > /// <!ELEMENT propfind ( propname | (allprop, include?) | prop ) >
#[derive(Debug, PartialEq)] #[derive(Debug, PartialEq, Clone)]
pub enum PropFind<E: Extension> { pub enum PropFind<E: Extension> {
PropName, PropName,
AllProp(Option<Include<E>>), AllProp(Option<Include<E>>),
@ -462,7 +462,7 @@ pub enum PropFind<E: Extension> {
/// the properties named in 'prop'. /// the properties named in 'prop'.
/// ///
/// <!ELEMENT propstat (prop, status, error?, responsedescription?) > /// <!ELEMENT propstat (prop, status, error?, responsedescription?) >
#[derive(Debug, PartialEq)] #[derive(Debug, PartialEq, Clone)]
pub struct PropStat<E: Extension, N: xml::Node<N>> { pub struct PropStat<E: Extension, N: xml::Node<N>> {
pub prop: N, pub prop: N,
pub status: Status, pub status: Status,
@ -483,7 +483,7 @@ pub struct PropStat<E: Extension, N: xml::Node<N>> {
/// the names of properties to be removed are required. /// the names of properties to be removed are required.
/// ///
/// <!ELEMENT remove (prop) > /// <!ELEMENT remove (prop) >
#[derive(Debug, PartialEq)] #[derive(Debug, PartialEq, Clone)]
pub struct Remove<E: Extension>(pub PropName<E>); pub struct Remove<E: Extension>(pub PropName<E>);
/// 14.24. response XML Element /// 14.24. response XML Element
@ -511,7 +511,7 @@ pub struct Remove<E: Extension>(pub PropName<E>);
/// ///
/// --- rewritten as --- /// --- rewritten as ---
/// <!ELEMENT response ((href+, status)|(href, propstat+), error?, responsedescription?, location?> /// <!ELEMENT response ((href+, status)|(href, propstat+), error?, responsedescription?, location?>
#[derive(Debug, PartialEq)] #[derive(Debug, PartialEq, Clone)]
pub enum StatusOrPropstat<E: Extension, N: xml::Node<N>> { pub enum StatusOrPropstat<E: Extension, N: xml::Node<N>> {
// One status, multiple hrefs... // One status, multiple hrefs...
Status(Vec<Href>, Status), Status(Vec<Href>, Status),
@ -519,7 +519,7 @@ pub enum StatusOrPropstat<E: Extension, N: xml::Node<N>> {
PropStat(Href, Vec<PropStat<E, N>>), PropStat(Href, Vec<PropStat<E, N>>),
} }
#[derive(Debug, PartialEq)] #[derive(Debug, PartialEq, Clone)]
pub struct Response<E: Extension, N: xml::Node<N>> { pub struct Response<E: Extension, N: xml::Node<N>> {
pub status_or_propstat: StatusOrPropstat<E, N>, pub status_or_propstat: StatusOrPropstat<E, N>,
pub error: Option<Error<E>>, pub error: Option<Error<E>>,
@ -538,7 +538,7 @@ pub struct Response<E: Extension, N: xml::Node<N>> {
/// user. /// user.
/// ///
/// <!ELEMENT responsedescription (#PCDATA) > /// <!ELEMENT responsedescription (#PCDATA) >
#[derive(Debug, PartialEq)] #[derive(Debug, PartialEq, Clone)]
pub struct ResponseDescription(pub String); pub struct ResponseDescription(pub String);
/// 14.26. set XML Element /// 14.26. set XML Element
@ -557,7 +557,7 @@ pub struct ResponseDescription(pub String);
/// property, and MUST be subsequently retrievable using PROPFIND. /// property, and MUST be subsequently retrievable using PROPFIND.
/// ///
/// <!ELEMENT set (prop) > /// <!ELEMENT set (prop) >
#[derive(Debug, PartialEq)] #[derive(Debug, PartialEq, Clone)]
pub struct Set<E: Extension>(pub PropValue<E>); pub struct Set<E: Extension>(pub PropValue<E>);
/// 14.27. shared XML Element /// 14.27. shared XML Element
@ -568,7 +568,7 @@ pub struct Set<E: Extension>(pub PropValue<E>);
/// ///
/// ///
/// <!ELEMENT shared EMPTY > /// <!ELEMENT shared EMPTY >
#[derive(Debug, PartialEq)] #[derive(Debug, PartialEq, Clone)]
pub struct Shared {} pub struct Shared {}
@ -582,7 +582,7 @@ pub struct Shared {}
/// ///
/// <!ELEMENT status (#PCDATA) > /// <!ELEMENT status (#PCDATA) >
//@FIXME: Better typing is possible with an enum for example //@FIXME: Better typing is possible with an enum for example
#[derive(Debug, PartialEq)] #[derive(Debug, PartialEq, Clone)]
pub struct Status(pub http::status::StatusCode); pub struct Status(pub http::status::StatusCode);
/// 14.29. timeout XML Element /// 14.29. timeout XML Element
@ -610,7 +610,7 @@ pub struct Status(pub http::status::StatusCode);
/// elapse between granting of the lock at the server, and the automatic /// elapse between granting of the lock at the server, and the automatic
/// removal of the lock. The timeout value for TimeType "Second" MUST /// removal of the lock. The timeout value for TimeType "Second" MUST
/// NOT be greater than 2^32-1. /// NOT be greater than 2^32-1.
#[derive(Debug, PartialEq)] #[derive(Debug, PartialEq, Clone)]
pub enum Timeout { pub enum Timeout {
Seconds(u32), Seconds(u32),
Infinite, Infinite,
@ -644,7 +644,7 @@ pub enum Timeout {
/// the header value could include LWS as defined in [RFC2616], Section /// the header value could include LWS as defined in [RFC2616], Section
/// 4.2. Server implementors SHOULD strip LWS from these values before /// 4.2. Server implementors SHOULD strip LWS from these values before
/// using as WebDAV property values. /// using as WebDAV property values.
#[derive(Debug, PartialEq)] #[derive(Debug, PartialEq, Clone)]
pub enum PropertyRequest<E: Extension> { pub enum PropertyRequest<E: Extension> {
CreationDate, CreationDate,
DisplayName, DisplayName,
@ -659,7 +659,7 @@ pub enum PropertyRequest<E: Extension> {
Extension(E::PropertyRequest), Extension(E::PropertyRequest),
} }
#[derive(Debug, PartialEq)] #[derive(Debug, PartialEq, Clone)]
pub enum Property<E: Extension> { pub enum Property<E: Extension> {
/// 15.1. creationdate Property /// 15.1. creationdate Property
/// ///
@ -942,7 +942,7 @@ pub enum Property<E: Extension> {
Extension(E::Property), Extension(E::Property),
} }
#[derive(Debug, PartialEq)] #[derive(Debug, PartialEq, Clone)]
pub enum ResourceType<E: Extension> { pub enum ResourceType<E: Extension> {
Collection, Collection,
Extension(E::ResourceType), Extension(E::ResourceType),

View file

@ -24,7 +24,7 @@ pub trait QRead<T> {
} }
// The representation of an XML node in Rust // The representation of an XML node in Rust
pub trait Node<T> = QRead<T> + QWrite + std::fmt::Debug + PartialEq + Sync; pub trait Node<T> = QRead<T> + QWrite + std::fmt::Debug + PartialEq + Clone + Sync;
// --------------- // ---------------

View file

@ -22,9 +22,9 @@ use rustls_pemfile::{certs, private_key};
use aero_user::config::{DavConfig, DavUnsecureConfig}; use aero_user::config::{DavConfig, DavUnsecureConfig};
use aero_user::login::ArcLoginProvider; use aero_user::login::ArcLoginProvider;
use aero_collections::user::User; use aero_collections::user::User;
use aero_dav::types::{PropFind, Multistatus, PropValue, ResponseDescription}; use aero_dav::types as dav;
use aero_dav::realization::{Core, Calendar}; use aero_dav::realization::Calendar;
use aero_dav::xml as dav; use aero_dav::xml as dxml;
pub struct Server { pub struct Server {
bind_addr: SocketAddr, bind_addr: SocketAddr,
@ -196,7 +196,13 @@ async fn router(user: std::sync::Arc<User>, req: Request<Incoming>) -> Result<Re
let path_segments: Vec<_> = path.split("/").filter(|s| *s != "").collect(); let path_segments: Vec<_> = path.split("/").filter(|s| *s != "").collect();
let method = req.method().as_str().to_uppercase(); let method = req.method().as_str().to_uppercase();
//@FIXME check depth, handle it
match (method.as_str(), path_segments.as_slice()) { match (method.as_str(), path_segments.as_slice()) {
("OPTIONS", _) => return Ok(Response::builder()
.status(200)
.header("DAV", "1")
.body(text_body(""))?),
("PROPFIND", []) => propfind_root(user, req).await, ("PROPFIND", []) => propfind_root(user, req).await,
(_, [ username, ..]) if *username != user.username => return Ok(Response::builder() (_, [ username, ..]) if *username != user.username => return Ok(Response::builder()
.status(403) .status(403)
@ -216,14 +222,73 @@ async fn router(user: std::sync::Arc<User>, req: Request<Incoming>) -> Result<Re
/// </D:prop></D:propfind> /// </D:prop></D:propfind>
async fn propfind_root(user: std::sync::Arc<User>, req: Request<Incoming>) -> Result<Response<BoxBody<Bytes, std::io::Error>>> { async fn propfind_root(user: std::sync::Arc<User>, req: Request<Incoming>) -> Result<Response<BoxBody<Bytes, std::io::Error>>> {
tracing::info!("root"); let supported_propname = vec![
dav::PropertyRequest::DisplayName,
dav::PropertyRequest::ResourceType,
];
let r = deserialize::<PropFind<Core>>(req).await?; // A client may choose not to submit a request body. An empty PROPFIND
println!("r: {:?}", r); // request body MUST be treated as if it were an 'allprop' request.
serialize(Multistatus::<Core, PropValue<Core>> { // @FIXME here we handle any invalid data as an allprop, an empty request is thus correctly
responses: vec![], // handled, but corrupted requests are also silently handled as allprop.
responsedescription: Some(ResponseDescription("hello world".to_string())), let propfind = deserialize::<dav::PropFind<Calendar>>(req).await.unwrap_or_else(|_| dav::PropFind::<Calendar>::AllProp(None));
}) tracing::debug!(recv=?propfind, "inferred propfind request");
if matches!(propfind, dav::PropFind::PropName) {
return serialize(dav::Multistatus::<Calendar, dav::PropName<Calendar>> {
responses: vec![dav::Response {
status_or_propstat: dav::StatusOrPropstat::PropStat(
dav::Href(format!("./{}/", user.username)),
vec![dav::PropStat {
prop: dav::PropName(supported_propname),
status: dav::Status(hyper::StatusCode::OK),
error: None,
responsedescription: None,
}],
),
error: None,
location: None,
responsedescription: Some(dav::ResponseDescription("user home directory".into())),
}],
responsedescription: Some(dav::ResponseDescription("propname response".to_string())),
});
}
let propname = match propfind {
dav::PropFind::PropName => unreachable!(),
dav::PropFind::AllProp(None) => supported_propname.clone(),
dav::PropFind::AllProp(Some(dav::Include(mut include))) => {
include.extend_from_slice(supported_propname.as_slice());
include
},
dav::PropFind::Prop(dav::PropName(inner)) => inner,
};
let values = propname.iter().filter_map(|n| match n {
dav::PropertyRequest::DisplayName => Some(dav::Property::DisplayName(format!("{} home", user.username))),
dav::PropertyRequest::ResourceType => Some(dav::Property::ResourceType(vec![dav::ResourceType::Collection])),
_ => None,
}).collect();
let multistatus = dav::Multistatus::<Calendar, dav::PropValue<Calendar>> {
responses: vec![ dav::Response {
status_or_propstat: dav::StatusOrPropstat::PropStat(
dav::Href(format!("./{}/", user.username)),
vec![dav::PropStat {
prop: dav::PropValue(values),
status: dav::Status(hyper::StatusCode::OK),
error: None,
responsedescription: None,
}],
),
error: None,
location: None,
responsedescription: Some(dav::ResponseDescription("Root node".into())),
} ],
responsedescription: Some(dav::ResponseDescription("hello world".to_string())),
};
serialize(multistatus)
} }
async fn propfind_home(user: std::sync::Arc<User>, req: &Request<impl hyper::body::Body>) -> Result<Response<BoxBody<Bytes, std::io::Error>>> { async fn propfind_home(user: std::sync::Arc<User>, req: &Request<impl hyper::body::Body>) -> Result<Response<BoxBody<Bytes, std::io::Error>>> {
@ -263,7 +328,7 @@ async fn collections(_user: std::sync::Arc<User>, _req: Request<impl hyper::body
use futures::stream::TryStreamExt; use futures::stream::TryStreamExt;
use http_body_util::{BodyStream, Empty}; use http_body_util::BodyStream;
use http_body_util::StreamBody; use http_body_util::StreamBody;
use http_body_util::combinators::BoxBody; use http_body_util::combinators::BoxBody;
use hyper::body::Frame; use hyper::body::Frame;
@ -277,7 +342,7 @@ fn text_body(txt: &'static str) -> BoxBody<Bytes, std::io::Error> {
BoxBody::new(Full::new(Bytes::from(txt)).map_err(|e| match e {})) BoxBody::new(Full::new(Bytes::from(txt)).map_err(|e| match e {}))
} }
fn serialize<T: dav::QWrite + Send + 'static>(elem: T) -> Result<Response<BoxBody<Bytes, std::io::Error>>> { fn serialize<T: dxml::QWrite + Send + 'static>(elem: T) -> Result<Response<BoxBody<Bytes, std::io::Error>>> {
let (tx, rx) = tokio::sync::mpsc::channel::<Bytes>(1); let (tx, rx) = tokio::sync::mpsc::channel::<Bytes>(1);
// Build the writer // Build the writer
@ -286,7 +351,7 @@ fn serialize<T: dav::QWrite + Send + 'static>(elem: T) -> Result<Response<BoxBod
let mut writer = SinkWriter::new(CopyToBytes::new(sink)); let mut writer = SinkWriter::new(CopyToBytes::new(sink));
let q = quick_xml::writer::Writer::new_with_indent(&mut writer, b' ', 4); let q = quick_xml::writer::Writer::new_with_indent(&mut writer, b' ', 4);
let ns_to_apply = vec![ ("xmlns:D".into(), "DAV:".into()) ]; let ns_to_apply = vec![ ("xmlns:D".into(), "DAV:".into()) ];
let mut qwriter = dav::Writer { q, ns_to_apply }; let mut qwriter = dxml::Writer { q, ns_to_apply };
match elem.qwrite(&mut qwriter).await { match elem.qwrite(&mut qwriter).await {
Ok(_) => tracing::debug!("fully serialized object"), Ok(_) => tracing::debug!("fully serialized object"),
Err(e) => tracing::error!(err=?e, "failed to serialize object"), Err(e) => tracing::error!(err=?e, "failed to serialize object"),
@ -308,14 +373,14 @@ fn serialize<T: dav::QWrite + Send + 'static>(elem: T) -> Result<Response<BoxBod
/// Deserialize a request body to an XML request /// Deserialize a request body to an XML request
async fn deserialize<T: dav::Node<T>>(req: Request<Incoming>) -> Result<T> { async fn deserialize<T: dxml::Node<T>>(req: Request<Incoming>) -> Result<T> {
let stream_of_frames = BodyStream::new(req.into_body()); let stream_of_frames = BodyStream::new(req.into_body());
let stream_of_bytes = stream_of_frames let stream_of_bytes = stream_of_frames
.try_filter_map(|frame| async move { Ok(frame.into_data().ok()) }) .try_filter_map(|frame| async move { Ok(frame.into_data().ok()) })
.map_err(|err| std::io::Error::new(std::io::ErrorKind::Other, err)); .map_err(|err| std::io::Error::new(std::io::ErrorKind::Other, err));
let async_read = tokio_util::io::StreamReader::new(stream_of_bytes); let async_read = tokio_util::io::StreamReader::new(stream_of_bytes);
let async_read = std::pin::pin!(async_read); let async_read = std::pin::pin!(async_read);
let mut rdr = dav::Reader::new(quick_xml::reader::NsReader::from_reader(async_read)).await?; let mut rdr = dxml::Reader::new(quick_xml::reader::NsReader::from_reader(async_read)).await?;
let parsed = rdr.find::<T>().await?; let parsed = rdr.find::<T>().await?;
Ok(parsed) Ok(parsed)
} }