diff options
-rw-r--r-- | aero-dav/src/caltypes.rs | 72 | ||||
-rw-r--r-- | aero-dav/src/realization.rs | 6 | ||||
-rw-r--r-- | aero-dav/src/types.rs | 66 | ||||
-rw-r--r-- | aero-dav/src/xml.rs | 2 | ||||
-rw-r--r-- | aero-proto/src/dav.rs | 95 |
5 files changed, 153 insertions, 88 deletions
diff --git a/aero-dav/src/caltypes.rs b/aero-dav/src/caltypes.rs index 628ec4b..cb0a98c 100644 --- a/aero-dav/src/caltypes.rs +++ b/aero-dav/src/caltypes.rs @@ -108,13 +108,13 @@ pub struct CalendarMultiget<E: dav::Extension> { pub struct FreeBusyQuery(pub TimeRange); // ----- Hooks ----- -#[derive(Debug, PartialEq)] +#[derive(Debug, PartialEq, Clone)] pub enum ResourceType { Calendar, } /// Check the matching Property object for documentation -#[derive(Debug, PartialEq)] +#[derive(Debug, PartialEq, Clone)] pub enum PropertyRequest { CalendarDescription, CalendarTimezone, @@ -129,7 +129,7 @@ pub enum PropertyRequest { CalendarData(CalendarDataRequest), } -#[derive(Debug, PartialEq)] +#[derive(Debug, PartialEq, Clone)] pub enum Property { /// Name: calendar-description /// @@ -609,7 +609,7 @@ pub enum Property { CalendarData(CalendarDataPayload), } -#[derive(Debug, PartialEq)] +#[derive(Debug, PartialEq, Clone)] pub enum Violation { /// (DAV:resource-must-be-null): A resource MUST NOT exist at the /// Request-URI; @@ -780,7 +780,7 @@ pub enum Violation { /// If the client chooses a collation not supported by the server, the /// server MUST respond with a CALDAV:supported-collation precondition /// error response. -#[derive(Debug, PartialEq)] +#[derive(Debug, PartialEq, Clone)] pub struct SupportedCollation(pub Collation); /// <!ELEMENT calendar-data (#PCDATA)> @@ -789,7 +789,7 @@ pub struct SupportedCollation(pub Collation); /// when nested in the DAV:prop XML element in a calendaring /// REPORT response to specify the content of a returned /// calendar object resource. -#[derive(Debug, PartialEq)] +#[derive(Debug, PartialEq, Clone)] pub struct CalendarDataPayload { pub mime: Option<CalendarDataSupport>, pub payload: String, @@ -802,7 +802,7 @@ pub struct CalendarDataPayload { /// when nested in the DAV:prop XML element in a calendaring /// REPORT request to specify which parts of calendar object /// resources should be returned in the response; -#[derive(Debug, PartialEq)] +#[derive(Debug, PartialEq, Clone)] pub struct CalendarDataRequest { pub mime: Option<CalendarDataSupport>, pub comp: Option<Comp>, @@ -817,7 +817,7 @@ pub struct CalendarDataRequest { /// when nested in the CALDAV:supported-calendar-data property /// to specify a supported media type for calendar object /// resources; -#[derive(Debug, PartialEq)] +#[derive(Debug, PartialEq, Clone)] pub struct CalendarDataEmpty(pub Option<CalendarDataSupport>); /// <!ATTLIST calendar-data content-type CDATA "text/calendar" @@ -826,7 +826,7 @@ pub struct CalendarDataEmpty(pub Option<CalendarDataSupport>); /// version value: a version string /// attributes can be used on all three variants of the /// CALDAV:calendar-data XML element. -#[derive(Debug, PartialEq)] +#[derive(Debug, PartialEq, Clone)] pub struct CalendarDataSupport { pub content_type: String, pub version: String, @@ -852,7 +852,7 @@ pub struct CalendarDataSupport { /// However, the CALDAV:prop and CALDAV:allprop elements are defined /// in the "urn:ietf:params:xml:ns:caldav" namespace instead of the /// "DAV:" namespace. -#[derive(Debug, PartialEq)] +#[derive(Debug, PartialEq, Clone)] pub struct Comp { pub name: Component, pub prop_kind: Option<PropKind>, @@ -872,7 +872,7 @@ pub struct Comp { /// <C:comp name="VEVENT"/> /// <C:comp name="VTODO"/> /// </C:supported-calendar-component-set> -#[derive(Debug, PartialEq)] +#[derive(Debug, PartialEq, Clone)] pub struct CompSupport(pub Component); /// Name: allcomp @@ -888,7 +888,7 @@ pub struct CompSupport(pub Component); /// Definition: /// /// <!ELEMENT allcomp EMPTY> -#[derive(Debug, PartialEq)] +#[derive(Debug, PartialEq, Clone)] pub enum CompKind { AllComp, Comp(Vec<Comp>), @@ -912,7 +912,7 @@ pub enum CompKind { /// allprop element defined in [RFC2518]. However, the CALDAV:allprop /// element is defined in the "urn:ietf:params:xml:ns:caldav" /// namespace instead of the "DAV:" namespace. -#[derive(Debug, PartialEq)] +#[derive(Debug, PartialEq, Clone)] pub enum PropKind { AllProp, Prop(Vec<CalProp>), @@ -942,13 +942,13 @@ pub enum PropKind { /// element defined in [RFC2518]. However, the CALDAV:prop element is /// defined in the "urn:ietf:params:xml:ns:caldav" namespace instead /// of the "DAV:" namespace. -#[derive(Debug, PartialEq)] +#[derive(Debug, PartialEq, Clone)] pub struct CalProp { pub name: ComponentProperty, pub novalue: Option<bool>, } -#[derive(Debug, PartialEq)] +#[derive(Debug, PartialEq, Clone)] pub enum RecurrenceModifier { Expand(Expand), LimitRecurrenceSet(LimitRecurrenceSet), @@ -994,7 +994,7 @@ pub enum RecurrenceModifier { /// end CDATA #REQUIRED> /// start 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>); /// CALDAV:limit-recurrence-set XML Element @@ -1042,7 +1042,7 @@ pub struct Expand(pub DateTime<Utc>, pub DateTime<Utc>); /// end CDATA #REQUIRED> /// start 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>); /// Name: limit-freebusy-set @@ -1073,11 +1073,11 @@ pub struct LimitRecurrenceSet(pub DateTime<Utc>, pub DateTime<Utc>); /// end CDATA #REQUIRED> /// start 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>); /// Used by CalendarQuery & CalendarMultiget -#[derive(Debug, PartialEq)] +#[derive(Debug, PartialEq, Clone)] pub enum CalendarSelector<E: dav::Extension> { AllProp, PropName, @@ -1135,20 +1135,20 @@ pub enum CalendarSelector<E: dav::Extension> { /// name value: a calendar object or calendar component /// type (e.g., VEVENT) /// ``` -#[derive(Debug, PartialEq)] +#[derive(Debug, PartialEq, Clone)] pub struct CompFilter { pub name: Component, // Option 1 = None, Option 2, 3, 4 = Some pub additional_rules: Option<CompFilterRules>, } -#[derive(Debug, PartialEq)] +#[derive(Debug, PartialEq, Clone)] pub enum CompFilterRules { // Option 2 IsNotDefined, // Options 3 & 4 Matches(CompFilterMatch), } -#[derive(Debug, PartialEq)] +#[derive(Debug, PartialEq, Clone)] pub struct CompFilterMatch { pub time_range: Option<TimeRange>, pub prop_filter: Vec<PropFilter>, @@ -1201,26 +1201,26 @@ pub struct CompFilterMatch { /// <!ATTLIST prop-filter name CDATA #REQUIRED> /// name value: a calendar property name (e.g., ATTENDEE) /// ``` -#[derive(Debug, PartialEq)] +#[derive(Debug, PartialEq, Clone)] pub struct PropFilter { pub name: ComponentProperty, // None = Option 1, Some() = Option 2, 3 & 4 pub additional_rules: Option<PropFilterRules>, } -#[derive(Debug, PartialEq)] +#[derive(Debug, PartialEq, Clone)] pub enum PropFilterRules { // Option 2 IsNotDefined, // Options 3 & 4 Match(PropFilterMatch), } -#[derive(Debug, PartialEq)] +#[derive(Debug, PartialEq, Clone)] pub struct PropFilterMatch { pub time_range: Option<TimeRange>, pub time_or_text: Option<TimeOrText>, pub param_filter: Vec<ParamFilter>, } -#[derive(Debug, PartialEq)] +#[derive(Debug, PartialEq, Clone)] pub enum TimeOrText { Time(TimeRange), Text(TextMatch), @@ -1254,7 +1254,7 @@ pub enum TimeOrText { /// PCDATA value: string /// <!ATTLIST text-match collation CDATA "i;ascii-casemap" /// negate-condition (yes | no) "no"> -#[derive(Debug, PartialEq)] +#[derive(Debug, PartialEq, Clone)] pub struct TextMatch { pub collation: Option<Collation>, pub negate_condition: Option<bool>, @@ -1292,12 +1292,12 @@ pub struct TextMatch { /// <!ATTLIST param-filter name CDATA #REQUIRED> /// name value: a property parameter name (e.g., PARTSTAT) /// ``` -#[derive(Debug, PartialEq)] +#[derive(Debug, PartialEq, Clone)] pub struct ParamFilter { pub name: PropertyParameter, pub additional_rules: Option<ParamFilterMatch>, } -#[derive(Debug, PartialEq)] +#[derive(Debug, PartialEq, Clone)] pub enum ParamFilterMatch { IsNotDefined, Match(TextMatch), @@ -1353,7 +1353,7 @@ pub enum ParamFilterMatch { /// /// <!ELEMENT timezone (#PCDATA)> /// PCDATA value: an iCalendar object with exactly one VTIMEZONE -#[derive(Debug, PartialEq)] +#[derive(Debug, PartialEq, Clone)] pub struct TimeZone(pub String); /// Name: filter @@ -1369,7 +1369,7 @@ pub struct TimeZone(pub String); /// /// Definition: /// <!ELEMENT filter (comp-filter)> -#[derive(Debug, PartialEq)] +#[derive(Debug, PartialEq, Clone)] pub struct Filter(pub CompFilter); /// Name: time-range @@ -1381,7 +1381,7 @@ pub struct Filter(pub CompFilter); /// end CDATA #IMPLIED> /// start 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 { OnlyStart(DateTime<Utc>), OnlyEnd(DateTime<Utc>), @@ -1391,7 +1391,7 @@ pub enum TimeRange { // ----------------------- ENUM ATTRIBUTES --------------------- /// Known components -#[derive(Debug, PartialEq)] +#[derive(Debug, PartialEq, Clone)] pub enum Component { VCalendar, VJournal, @@ -1432,11 +1432,11 @@ impl Component { /// name="VERSION", name="SUMMARY", etc. /// Can be set on different objects: VCalendar, VEvent, etc. /// Might be replaced by an enum later -#[derive(Debug, PartialEq)] +#[derive(Debug, PartialEq, Clone)] pub struct ComponentProperty(pub String); /// like PARSTAT -#[derive(Debug, PartialEq)] +#[derive(Debug, PartialEq, Clone)] pub struct PropertyParameter(pub String); impl PropertyParameter { 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 { #[default] AsciiCaseMap, diff --git a/aero-dav/src/realization.rs b/aero-dav/src/realization.rs index a7bbb16..8c47fad 100644 --- a/aero-dav/src/realization.rs +++ b/aero-dav/src/realization.rs @@ -3,7 +3,7 @@ use super::caltypes as cal; use super::xml; use super::error; -#[derive(Debug, PartialEq)] +#[derive(Debug, PartialEq, Clone)] pub struct Disabled(()); impl xml::QRead<Disabled> for Disabled { 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 /// due to a private inner element. -#[derive(Debug, PartialEq)] +#[derive(Debug, PartialEq, Clone)] pub struct Core {} impl dav::Extension for Core { type Error = Disabled; @@ -30,7 +30,7 @@ impl dav::Extension for Core { } // WebDAV with the base Calendar implementation (RFC4791) -#[derive(Debug, PartialEq)] +#[derive(Debug, PartialEq, Clone)] pub struct Calendar {} impl dav::Extension for Calendar { diff --git a/aero-dav/src/types.rs b/aero-dav/src/types.rs index 2489c0a..79e98fd 100644 --- a/aero-dav/src/types.rs +++ b/aero-dav/src/types.rs @@ -6,7 +6,7 @@ use super::xml; /// It's how we implement a DAV extension /// (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 Property: xml::Node<Self::Property>; type PropertyRequest: xml::Node<Self::PropertyRequest>; @@ -20,7 +20,7 @@ pub trait Extension: std::fmt::Debug + PartialEq { /// Purpose: Describes a lock on a resource. /// <!ELEMENT activelock (lockscope, locktype, depth, owner?, timeout?, /// locktoken?, lockroot)> -#[derive(Debug, PartialEq)] +#[derive(Debug, PartialEq, Clone)] pub struct ActiveLock { pub lockscope: LockScope, pub locktype: LockType, @@ -54,7 +54,7 @@ pub struct Collection{} /// Value: "0" | "1" | "infinity" /// /// <!ELEMENT depth (#PCDATA) > -#[derive(Debug, PartialEq)] +#[derive(Debug, PartialEq, Clone)] pub enum Depth { Zero, One, @@ -77,9 +77,9 @@ pub enum Depth { /// postcondition code. Unrecognized elements MUST be ignored. /// /// <!ELEMENT error ANY > -#[derive(Debug, PartialEq)] +#[derive(Debug, PartialEq, Clone)] pub struct Error<E: Extension>(pub Vec<Violation<E>>); -#[derive(Debug, PartialEq)] +#[derive(Debug, PartialEq, Clone)] pub enum Violation<E: Extension> { /// Name: lock-token-matches-request-uri /// @@ -190,7 +190,7 @@ pub struct Exclusive {} /// Value: Simple-ref /// /// <!ELEMENT href (#PCDATA)> -#[derive(Debug, PartialEq)] +#[derive(Debug, PartialEq, Clone)] pub struct Href(pub String); @@ -206,7 +206,7 @@ pub struct Href(pub String); /// standards. This element MUST NOT contain text or mixed content. /// /// <!ELEMENT include ANY > -#[derive(Debug, PartialEq)] +#[derive(Debug, PartialEq, Clone)] pub struct Include<E: Extension>(pub Vec<PropertyRequest<E>>); /// 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. /// /// <!ELEMENT location (href)> -#[derive(Debug, PartialEq)] +#[derive(Debug, PartialEq, Clone)] pub struct Location(pub Href); /// 14.10. lockentry XML Element @@ -234,7 +234,7 @@ pub struct Location(pub Href); /// resource. /// /// <!ELEMENT lockentry (lockscope, locktype) > -#[derive(Debug, PartialEq)] +#[derive(Debug, PartialEq, Clone)] pub struct LockEntry { pub lockscope: LockScope, pub locktype: LockType, @@ -248,7 +248,7 @@ pub struct LockEntry { /// specify the type of lock the client wishes to have created. /// /// <!ELEMENT lockinfo (lockscope, locktype, owner?) > -#[derive(Debug, PartialEq)] +#[derive(Debug, PartialEq, Clone)] pub struct LockInfo { pub lockscope: LockScope, pub locktype: LockType, @@ -267,7 +267,7 @@ pub struct LockInfo { /// values and the response to LOCK requests. /// /// <!ELEMENT lockroot (href) > -#[derive(Debug, PartialEq)] +#[derive(Debug, PartialEq, Clone)] pub struct LockRoot(pub Href); /// 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 /// lock. /// <!ELEMENT lockscope (exclusive | shared) > -#[derive(Debug, PartialEq)] +#[derive(Debug, PartialEq, Clone)] pub enum LockScope { Exclusive, Shared @@ -293,7 +293,7 @@ pub enum LockScope { /// refers to the lock. /// /// <!ELEMENT locktoken (href) > -#[derive(Debug, PartialEq)] +#[derive(Debug, PartialEq, Clone)] pub struct LockToken(pub Href); /// 14.15. locktype XML Element @@ -304,7 +304,7 @@ pub struct LockToken(pub Href); /// specification only defines one lock type, the write lock. /// /// <!ELEMENT locktype (write) > -#[derive(Debug, PartialEq)] +#[derive(Debug, PartialEq, Clone)] pub enum LockType { /// 14.30. write XML Element /// @@ -330,7 +330,7 @@ pub enum LockType { /// response descriptions contained within the responses. /// /// <!ELEMENT multistatus (response*, responsedescription?) > -#[derive(Debug, PartialEq)] +#[derive(Debug, PartialEq, Clone)] pub struct Multistatus<E: Extension, N: xml::Node<N>> { pub responses: Vec<Response<E, N>>, pub responsedescription: Option<ResponseDescription>, @@ -360,7 +360,7 @@ pub struct Multistatus<E: Extension, N: xml::Node<N>> { /// /// <!ELEMENT owner ANY > //@FIXME might need support for an extension -#[derive(Debug, PartialEq)] +#[derive(Debug, PartialEq, Clone)] pub enum Owner { Txt(String), Href(Href), @@ -381,10 +381,10 @@ pub enum Owner { /// text or mixed content. /// /// <!ELEMENT prop ANY > -#[derive(Debug, PartialEq)] +#[derive(Debug, PartialEq, Clone)] 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>>); /// 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. /// /// <!ELEMENT propertyupdate (remove | set)+ > -#[derive(Debug, PartialEq)] +#[derive(Debug, PartialEq, Clone)] pub struct PropertyUpdate<E: Extension>(pub Vec<PropertyUpdateItem<E>>); -#[derive(Debug, PartialEq)] +#[derive(Debug, PartialEq, Clone)] pub enum PropertyUpdateItem<E: Extension> { Remove(Remove<E>), Set(Set<E>), @@ -440,7 +440,7 @@ pub enum PropertyUpdateItem<E: Extension> { /// values. /// /// <!ELEMENT propfind ( propname | (allprop, include?) | prop ) > -#[derive(Debug, PartialEq)] +#[derive(Debug, PartialEq, Clone)] pub enum PropFind<E: Extension> { PropName, AllProp(Option<Include<E>>), @@ -462,7 +462,7 @@ pub enum PropFind<E: Extension> { /// the properties named in 'prop'. /// /// <!ELEMENT propstat (prop, status, error?, responsedescription?) > -#[derive(Debug, PartialEq)] +#[derive(Debug, PartialEq, Clone)] pub struct PropStat<E: Extension, N: xml::Node<N>> { pub prop: N, 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. /// /// <!ELEMENT remove (prop) > -#[derive(Debug, PartialEq)] +#[derive(Debug, PartialEq, Clone)] pub struct Remove<E: Extension>(pub PropName<E>); /// 14.24. response XML Element @@ -511,7 +511,7 @@ pub struct Remove<E: Extension>(pub PropName<E>); /// /// --- rewritten as --- /// <!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>> { // One status, multiple hrefs... Status(Vec<Href>, Status), @@ -519,7 +519,7 @@ pub enum StatusOrPropstat<E: Extension, N: xml::Node<N>> { PropStat(Href, Vec<PropStat<E, N>>), } -#[derive(Debug, PartialEq)] +#[derive(Debug, PartialEq, Clone)] pub struct Response<E: Extension, N: xml::Node<N>> { pub status_or_propstat: StatusOrPropstat<E, N>, pub error: Option<Error<E>>, @@ -538,7 +538,7 @@ pub struct Response<E: Extension, N: xml::Node<N>> { /// user. /// /// <!ELEMENT responsedescription (#PCDATA) > -#[derive(Debug, PartialEq)] +#[derive(Debug, PartialEq, Clone)] pub struct ResponseDescription(pub String); /// 14.26. set XML Element @@ -557,7 +557,7 @@ pub struct ResponseDescription(pub String); /// property, and MUST be subsequently retrievable using PROPFIND. /// /// <!ELEMENT set (prop) > -#[derive(Debug, PartialEq)] +#[derive(Debug, PartialEq, Clone)] pub struct Set<E: Extension>(pub PropValue<E>); /// 14.27. shared XML Element @@ -568,7 +568,7 @@ pub struct Set<E: Extension>(pub PropValue<E>); /// /// /// <!ELEMENT shared EMPTY > -#[derive(Debug, PartialEq)] +#[derive(Debug, PartialEq, Clone)] pub struct Shared {} @@ -582,7 +582,7 @@ pub struct Shared {} /// /// <!ELEMENT status (#PCDATA) > //@FIXME: Better typing is possible with an enum for example -#[derive(Debug, PartialEq)] +#[derive(Debug, PartialEq, Clone)] pub struct Status(pub http::status::StatusCode); /// 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 /// removal of the lock. The timeout value for TimeType "Second" MUST /// NOT be greater than 2^32-1. -#[derive(Debug, PartialEq)] +#[derive(Debug, PartialEq, Clone)] pub enum Timeout { Seconds(u32), Infinite, @@ -644,7 +644,7 @@ pub enum Timeout { /// the header value could include LWS as defined in [RFC2616], Section /// 4.2. Server implementors SHOULD strip LWS from these values before /// using as WebDAV property values. -#[derive(Debug, PartialEq)] +#[derive(Debug, PartialEq, Clone)] pub enum PropertyRequest<E: Extension> { CreationDate, DisplayName, @@ -659,7 +659,7 @@ pub enum PropertyRequest<E: Extension> { Extension(E::PropertyRequest), } -#[derive(Debug, PartialEq)] +#[derive(Debug, PartialEq, Clone)] pub enum Property<E: Extension> { /// 15.1. creationdate Property /// @@ -942,7 +942,7 @@ pub enum Property<E: Extension> { Extension(E::Property), } -#[derive(Debug, PartialEq)] +#[derive(Debug, PartialEq, Clone)] pub enum ResourceType<E: Extension> { Collection, Extension(E::ResourceType), diff --git a/aero-dav/src/xml.rs b/aero-dav/src/xml.rs index e078c6f..827e9d0 100644 --- a/aero-dav/src/xml.rs +++ b/aero-dav/src/xml.rs @@ -24,7 +24,7 @@ pub trait QRead<T> { } // 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; // --------------- diff --git a/aero-proto/src/dav.rs b/aero-proto/src/dav.rs index 3981b61..0bbb7f7 100644 --- a/aero-proto/src/dav.rs +++ b/aero-proto/src/dav.rs @@ -22,9 +22,9 @@ use rustls_pemfile::{certs, private_key}; use aero_user::config::{DavConfig, DavUnsecureConfig}; use aero_user::login::ArcLoginProvider; use aero_collections::user::User; -use aero_dav::types::{PropFind, Multistatus, PropValue, ResponseDescription}; -use aero_dav::realization::{Core, Calendar}; -use aero_dav::xml as dav; +use aero_dav::types as dav; +use aero_dav::realization::Calendar; +use aero_dav::xml as dxml; pub struct Server { 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 method = req.method().as_str().to_uppercase(); + //@FIXME check depth, handle it + 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, (_, [ username, ..]) if *username != user.username => return Ok(Response::builder() .status(403) @@ -216,14 +222,73 @@ async fn router(user: std::sync::Arc<User>, req: Request<Incoming>) -> Result<Re /// </D:prop></D:propfind> 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, + ]; + + // A client may choose not to submit a request body. An empty PROPFIND + // request body MUST be treated as if it were an 'allprop' request. + // @FIXME here we handle any invalid data as an allprop, an empty request is thus correctly + // handled, but corrupted requests are also silently handled as allprop. + 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 r = deserialize::<PropFind<Core>>(req).await?; - println!("r: {:?}", r); - serialize(Multistatus::<Core, PropValue<Core>> { - responses: vec![], - responsedescription: Some(ResponseDescription("hello world".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>>> { @@ -263,7 +328,7 @@ async fn collections(_user: std::sync::Arc<User>, _req: Request<impl hyper::body use futures::stream::TryStreamExt; -use http_body_util::{BodyStream, Empty}; +use http_body_util::BodyStream; use http_body_util::StreamBody; use http_body_util::combinators::BoxBody; 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 {})) } -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); // 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 q = quick_xml::writer::Writer::new_with_indent(&mut writer, b' ', 4); 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 { Ok(_) => tracing::debug!("fully serialized 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 -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_bytes = stream_of_frames .try_filter_map(|frame| async move { Ok(frame.into_data().ok()) }) .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 = 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?; Ok(parsed) } |