successfully return ICS in REPORT queries
This commit is contained in:
parent
adbccd8834
commit
6de63055a2
3 changed files with 172 additions and 167 deletions
|
@ -135,7 +135,7 @@ impl Controller {
|
||||||
Some(cal::CalendarSelector::Prop(inner)) => Some(inner),
|
Some(cal::CalendarSelector::Prop(inner)) => Some(inner),
|
||||||
};
|
};
|
||||||
|
|
||||||
serialize(status, Self::multistatus(&self.user, ok_node, not_found, props))
|
serialize(status, Self::multistatus(&self.user, ok_node, not_found, props).await)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// PROPFIND is the standard way to fetch WebDAV properties
|
/// PROPFIND is the standard way to fetch WebDAV properties
|
||||||
|
@ -176,7 +176,7 @@ impl Controller {
|
||||||
|
|
||||||
// Not Found is currently impossible considering the way we designed this function
|
// Not Found is currently impossible considering the way we designed this function
|
||||||
let not_found = vec![];
|
let not_found = vec![];
|
||||||
serialize(status, Self::multistatus(&self.user, nodes, not_found, propname))
|
serialize(status, Self::multistatus(&self.user, nodes, not_found, propname).await)
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn put(self) -> Result<HttpResponse> {
|
async fn put(self) -> Result<HttpResponse> {
|
||||||
|
@ -204,7 +204,7 @@ impl Controller {
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn get(self) -> Result<HttpResponse> {
|
async fn get(self) -> Result<HttpResponse> {
|
||||||
let stream_body = StreamBody::new(self.node.content().await.map_ok(|v| Frame::data(v)));
|
let stream_body = StreamBody::new(self.node.content().map_ok(|v| Frame::data(v)));
|
||||||
let boxed_body = UnsyncBoxBody::new(stream_body);
|
let boxed_body = UnsyncBoxBody::new(stream_body);
|
||||||
|
|
||||||
let response = Response::builder()
|
let response = Response::builder()
|
||||||
|
@ -217,10 +217,10 @@ impl Controller {
|
||||||
|
|
||||||
// --- Common utility functions ---
|
// --- Common utility functions ---
|
||||||
/// Build a multistatus response from a list of DavNodes
|
/// Build a multistatus response from a list of DavNodes
|
||||||
fn multistatus(user: &ArcUser, nodes: Vec<Box<dyn DavNode>>, not_found: Vec<dav::Href>, props: Option<dav::PropName<All>>) -> dav::Multistatus<All> {
|
async fn multistatus(user: &ArcUser, nodes: Vec<Box<dyn DavNode>>, not_found: Vec<dav::Href>, props: Option<dav::PropName<All>>) -> dav::Multistatus<All> {
|
||||||
// Collect properties on existing objects
|
// Collect properties on existing objects
|
||||||
let mut responses: Vec<dav::Response<All>> = match props {
|
let mut responses: Vec<dav::Response<All>> = match props {
|
||||||
Some(props) => nodes.into_iter().map(|n| n.response_props(user, props.clone())).collect(),
|
Some(props) => futures::stream::iter(nodes).then(|n| n.response_props(user, props.clone())).collect().await,
|
||||||
None => nodes.into_iter().map(|n| n.response_propname(user)).collect(),
|
None => nodes.into_iter().map(|n| n.response_propname(user)).collect(),
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
use anyhow::Result;
|
use anyhow::Result;
|
||||||
use futures::stream::{BoxStream, Stream};
|
use futures::stream::{BoxStream, Stream, StreamExt};
|
||||||
use futures::future::BoxFuture;
|
use futures::future::{BoxFuture, FutureExt};
|
||||||
use hyper::body::Bytes;
|
use hyper::body::Bytes;
|
||||||
|
|
||||||
use aero_dav::types as dav;
|
use aero_dav::types as dav;
|
||||||
|
@ -10,6 +10,7 @@ use aero_collections::davdag::Etag;
|
||||||
use super::controller::ArcUser;
|
use super::controller::ArcUser;
|
||||||
|
|
||||||
pub(crate) type Content<'a> = BoxStream<'a, std::result::Result<Bytes, std::io::Error>>;
|
pub(crate) type Content<'a> = BoxStream<'a, std::result::Result<Bytes, std::io::Error>>;
|
||||||
|
pub(crate) type PropertyStream<'a> = BoxStream<'a, std::result::Result<dav::Property<All>, dav::PropertyRequest<All>>>;
|
||||||
|
|
||||||
pub(crate) enum PutPolicy {
|
pub(crate) enum PutPolicy {
|
||||||
CreateOnly,
|
CreateOnly,
|
||||||
|
@ -31,13 +32,14 @@ pub(crate) trait DavNode: Send {
|
||||||
/// Get the supported WebDAV properties
|
/// Get the supported WebDAV properties
|
||||||
fn supported_properties(&self, user: &ArcUser) -> dav::PropName<All>;
|
fn supported_properties(&self, user: &ArcUser) -> dav::PropName<All>;
|
||||||
/// Get the values for the given properties
|
/// Get the values for the given properties
|
||||||
fn properties(&self, user: &ArcUser, prop: dav::PropName<All>) -> Vec<dav::AnyProperty<All>>;
|
fn properties(&self, user: &ArcUser, prop: dav::PropName<All>) -> PropertyStream<'static>;
|
||||||
|
//fn properties(&self, user: &ArcUser, prop: dav::PropName<All>) -> Vec<dav::AnyProperty<All>>;
|
||||||
/// Put an element (create or update)
|
/// Put an element (create or update)
|
||||||
fn put<'a>(&'a self, policy: PutPolicy, stream: Content<'a>) -> BoxFuture<'a, Result<Etag>>;
|
fn put<'a>(&'a self, policy: PutPolicy, stream: Content<'a>) -> BoxFuture<'a, Result<Etag>>;
|
||||||
/// Content type of the element
|
/// Content type of the element
|
||||||
fn content_type(&self) -> &str;
|
fn content_type(&self) -> &str;
|
||||||
/// Get content
|
/// Get content
|
||||||
fn content<'a>(&'a self) -> BoxFuture<'a, Content<'static>>;
|
fn content(&self) -> Content<'static>;
|
||||||
|
|
||||||
//@FIXME maybe add etag, maybe add a way to set content
|
//@FIXME maybe add etag, maybe add a way to set content
|
||||||
|
|
||||||
|
@ -62,9 +64,20 @@ pub(crate) trait DavNode: Send {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Utility function to get a prop response from a node & a list of propname
|
/// Utility function to get a prop response from a node & a list of propname
|
||||||
fn response_props(&self, user: &ArcUser, props: dav::PropName<All>) -> dav::Response<All> {
|
fn response_props(&self, user: &ArcUser, props: dav::PropName<All>) -> BoxFuture<'static, dav::Response<All>> {
|
||||||
|
//@FIXME we should make the DAV parsed object a stream...
|
||||||
|
let mut result_stream = self.properties(user, props);
|
||||||
|
let path = self.path(user);
|
||||||
|
|
||||||
|
async move {
|
||||||
let mut prop_desc = vec![];
|
let mut prop_desc = vec![];
|
||||||
let (found, not_found): (Vec<_>, Vec<_>) = self.properties(user, props).into_iter().partition(|v| matches!(v, dav::AnyProperty::Value(_)));
|
let (mut found, mut not_found) = (vec![], vec![]);
|
||||||
|
while let Some(maybe_prop) = result_stream.next().await {
|
||||||
|
match maybe_prop {
|
||||||
|
Ok(v) => found.push(dav::AnyProperty::Value(v)),
|
||||||
|
Err(v) => not_found.push(dav::AnyProperty::Request(v)),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// If at least one property has been found on this object, adding a HTTP 200 propstat to
|
// If at least one property has been found on this object, adding a HTTP 200 propstat to
|
||||||
// the response
|
// the response
|
||||||
|
@ -90,11 +103,12 @@ pub(crate) trait DavNode: Send {
|
||||||
|
|
||||||
// Build the finale response
|
// Build the finale response
|
||||||
dav::Response {
|
dav::Response {
|
||||||
status_or_propstat: dav::StatusOrPropstat::PropStat(dav::Href(self.path(user)), prop_desc),
|
status_or_propstat: dav::StatusOrPropstat::PropStat(dav::Href(path), prop_desc),
|
||||||
error: None,
|
error: None,
|
||||||
location: None,
|
location: None,
|
||||||
responsedescription: None
|
responsedescription: None
|
||||||
}
|
}
|
||||||
|
}.boxed()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -13,6 +13,7 @@ use aero_dav::acltypes as acl;
|
||||||
use aero_dav::realization::{All, self as all};
|
use aero_dav::realization::{All, self as all};
|
||||||
|
|
||||||
use crate::dav::node::{DavNode, PutPolicy, Content};
|
use crate::dav::node::{DavNode, PutPolicy, Content};
|
||||||
|
use super::node::PropertyStream;
|
||||||
|
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
pub(crate) struct RootNode {}
|
pub(crate) struct RootNode {}
|
||||||
|
@ -48,27 +49,30 @@ impl DavNode for RootNode {
|
||||||
dav::PropertyRequest::Extension(all::PropertyRequest::Acl(acl::PropertyRequest::CurrentUserPrincipal)),
|
dav::PropertyRequest::Extension(all::PropertyRequest::Acl(acl::PropertyRequest::CurrentUserPrincipal)),
|
||||||
])
|
])
|
||||||
}
|
}
|
||||||
fn properties(&self, user: &ArcUser, prop: dav::PropName<All>) -> Vec<dav::AnyProperty<All>> {
|
|
||||||
prop.0.into_iter().map(|n| match n {
|
fn properties(&self, user: &ArcUser, prop: dav::PropName<All>) -> PropertyStream<'static> {
|
||||||
dav::PropertyRequest::DisplayName => dav::AnyProperty::Value(dav::Property::DisplayName("DAV Root".to_string())),
|
let user = user.clone();
|
||||||
dav::PropertyRequest::ResourceType => dav::AnyProperty::Value(dav::Property::ResourceType(vec![
|
futures::stream::iter(prop.0).map(move |n| {
|
||||||
|
let prop = match n {
|
||||||
|
dav::PropertyRequest::DisplayName => dav::Property::DisplayName("DAV Root".to_string()),
|
||||||
|
dav::PropertyRequest::ResourceType => dav::Property::ResourceType(vec![
|
||||||
dav::ResourceType::Collection,
|
dav::ResourceType::Collection,
|
||||||
])),
|
]),
|
||||||
dav::PropertyRequest::GetContentType => dav::AnyProperty::Value(dav::Property::GetContentType("httpd/unix-directory".into())),
|
dav::PropertyRequest::GetContentType => dav::Property::GetContentType("httpd/unix-directory".into()),
|
||||||
dav::PropertyRequest::Extension(all::PropertyRequest::Acl(acl::PropertyRequest::CurrentUserPrincipal)) =>
|
dav::PropertyRequest::Extension(all::PropertyRequest::Acl(acl::PropertyRequest::CurrentUserPrincipal)) =>
|
||||||
dav::AnyProperty::Value(dav::Property::Extension(all::Property::Acl(acl::Property::CurrentUserPrincipal(acl::User::Authenticated(dav::Href(HomeNode{}.path(user))))))),
|
dav::Property::Extension(all::Property::Acl(acl::Property::CurrentUserPrincipal(acl::User::Authenticated(dav::Href(HomeNode{}.path(&user)))))),
|
||||||
v => dav::AnyProperty::Request(v),
|
v => return Err(v),
|
||||||
}).collect()
|
};
|
||||||
|
Ok(prop)
|
||||||
|
}).boxed()
|
||||||
}
|
}
|
||||||
|
|
||||||
fn put<'a>(&'a self, _policy: PutPolicy, stream: Content<'a>) -> BoxFuture<'a, Result<Etag>> {
|
fn put<'a>(&'a self, _policy: PutPolicy, stream: Content<'a>) -> BoxFuture<'a, Result<Etag>> {
|
||||||
todo!()
|
todo!()
|
||||||
}
|
}
|
||||||
|
|
||||||
fn content<'a>(&'a self) -> BoxFuture<'a, Content<'static>> {
|
fn content(&self) -> Content<'static> {
|
||||||
async {
|
|
||||||
futures::stream::once(futures::future::err(std::io::Error::from(std::io::ErrorKind::Unsupported))).boxed()
|
futures::stream::once(futures::future::err(std::io::Error::from(std::io::ErrorKind::Unsupported))).boxed()
|
||||||
}.boxed()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn content_type(&self) -> &str {
|
fn content_type(&self) -> &str {
|
||||||
|
@ -116,32 +120,35 @@ impl DavNode for HomeNode {
|
||||||
dav::PropertyRequest::Extension(all::PropertyRequest::Cal(cal::PropertyRequest::CalendarHomeSet)),
|
dav::PropertyRequest::Extension(all::PropertyRequest::Cal(cal::PropertyRequest::CalendarHomeSet)),
|
||||||
])
|
])
|
||||||
}
|
}
|
||||||
fn properties(&self, user: &ArcUser, prop: dav::PropName<All>) -> Vec<dav::AnyProperty<All>> {
|
fn properties(&self, user: &ArcUser, prop: dav::PropName<All>) -> PropertyStream<'static> {
|
||||||
prop.0.into_iter().map(|n| match n {
|
let user = user.clone();
|
||||||
dav::PropertyRequest::DisplayName => dav::AnyProperty::Value(dav::Property::DisplayName(format!("{} home", user.username))),
|
|
||||||
dav::PropertyRequest::ResourceType => dav::AnyProperty::Value(dav::Property::ResourceType(vec![
|
futures::stream::iter(prop.0).map(move |n| {
|
||||||
|
let prop = match n {
|
||||||
|
dav::PropertyRequest::DisplayName => dav::Property::DisplayName(format!("{} home", user.username)),
|
||||||
|
dav::PropertyRequest::ResourceType => dav::Property::ResourceType(vec![
|
||||||
dav::ResourceType::Collection,
|
dav::ResourceType::Collection,
|
||||||
dav::ResourceType::Extension(all::ResourceType::Acl(acl::ResourceType::Principal)),
|
dav::ResourceType::Extension(all::ResourceType::Acl(acl::ResourceType::Principal)),
|
||||||
])),
|
]),
|
||||||
dav::PropertyRequest::GetContentType => dav::AnyProperty::Value(dav::Property::GetContentType("httpd/unix-directory".into())),
|
dav::PropertyRequest::GetContentType => dav::Property::GetContentType("httpd/unix-directory".into()),
|
||||||
dav::PropertyRequest::Extension(all::PropertyRequest::Cal(cal::PropertyRequest::CalendarHomeSet)) =>
|
dav::PropertyRequest::Extension(all::PropertyRequest::Cal(cal::PropertyRequest::CalendarHomeSet)) =>
|
||||||
dav::AnyProperty::Value(dav::Property::Extension(all::Property::Cal(cal::Property::CalendarHomeSet(dav::Href(
|
dav::Property::Extension(all::Property::Cal(cal::Property::CalendarHomeSet(dav::Href(
|
||||||
//@FIXME we are hardcoding the calendar path, instead we would want to use
|
//@FIXME we are hardcoding the calendar path, instead we would want to use
|
||||||
//objects
|
//objects
|
||||||
format!("/{}/calendar/", user.username)
|
format!("/{}/calendar/", user.username)
|
||||||
))))),
|
)))),
|
||||||
v => dav::AnyProperty::Request(v),
|
v => return Err(v),
|
||||||
}).collect()
|
};
|
||||||
|
Ok(prop)
|
||||||
|
}).boxed()
|
||||||
}
|
}
|
||||||
|
|
||||||
fn put<'a>(&'a self, _policy: PutPolicy, stream: Content<'a>) -> BoxFuture<'a, Result<Etag>> {
|
fn put<'a>(&'a self, _policy: PutPolicy, stream: Content<'a>) -> BoxFuture<'a, Result<Etag>> {
|
||||||
todo!()
|
todo!()
|
||||||
}
|
}
|
||||||
|
|
||||||
fn content<'a>(&'a self) -> BoxFuture<'a, Content<'static>> {
|
fn content(&self) -> Content<'static> {
|
||||||
async {
|
|
||||||
futures::stream::once(futures::future::err(std::io::Error::from(std::io::ErrorKind::Unsupported))).boxed()
|
futures::stream::once(futures::future::err(std::io::Error::from(std::io::ErrorKind::Unsupported))).boxed()
|
||||||
}.boxed()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -209,23 +216,26 @@ impl DavNode for CalendarListNode {
|
||||||
dav::PropertyRequest::GetContentType,
|
dav::PropertyRequest::GetContentType,
|
||||||
])
|
])
|
||||||
}
|
}
|
||||||
fn properties(&self, user: &ArcUser, prop: dav::PropName<All>) -> Vec<dav::AnyProperty<All>> {
|
fn properties(&self, user: &ArcUser, prop: dav::PropName<All>) -> PropertyStream<'static> {
|
||||||
prop.0.into_iter().map(|n| match n {
|
let user = user.clone();
|
||||||
dav::PropertyRequest::DisplayName => dav::AnyProperty::Value(dav::Property::DisplayName(format!("{} calendars", user.username))),
|
|
||||||
dav::PropertyRequest::ResourceType => dav::AnyProperty::Value(dav::Property::ResourceType(vec![dav::ResourceType::Collection])),
|
futures::stream::iter(prop.0).map(move |n| {
|
||||||
dav::PropertyRequest::GetContentType => dav::AnyProperty::Value(dav::Property::GetContentType("httpd/unix-directory".into())),
|
let prop = match n {
|
||||||
v => dav::AnyProperty::Request(v),
|
dav::PropertyRequest::DisplayName => dav::Property::DisplayName(format!("{} calendars", user.username)),
|
||||||
}).collect()
|
dav::PropertyRequest::ResourceType => dav::Property::ResourceType(vec![dav::ResourceType::Collection]),
|
||||||
|
dav::PropertyRequest::GetContentType => dav::Property::GetContentType("httpd/unix-directory".into()),
|
||||||
|
v => return Err(v),
|
||||||
|
};
|
||||||
|
Ok(prop)
|
||||||
|
}).boxed()
|
||||||
}
|
}
|
||||||
|
|
||||||
fn put<'a>(&'a self, _policy: PutPolicy, stream: Content<'a>) -> BoxFuture<'a, Result<Etag>> {
|
fn put<'a>(&'a self, _policy: PutPolicy, stream: Content<'a>) -> BoxFuture<'a, Result<Etag>> {
|
||||||
todo!()
|
todo!()
|
||||||
}
|
}
|
||||||
|
|
||||||
fn content<'a>(&'a self) -> BoxFuture<'a, Content<'static>> {
|
fn content(&self) -> Content<'static> {
|
||||||
async {
|
|
||||||
futures::stream::once(futures::future::err(std::io::Error::from(std::io::ErrorKind::Unsupported))).boxed()
|
futures::stream::once(futures::future::err(std::io::Error::from(std::io::ErrorKind::Unsupported))).boxed()
|
||||||
}.boxed()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn content_type(&self) -> &str {
|
fn content_type(&self) -> &str {
|
||||||
|
@ -300,32 +310,35 @@ impl DavNode for CalendarNode {
|
||||||
dav::PropertyRequest::Extension(all::PropertyRequest::Cal(cal::PropertyRequest::SupportedCalendarComponentSet)),
|
dav::PropertyRequest::Extension(all::PropertyRequest::Cal(cal::PropertyRequest::SupportedCalendarComponentSet)),
|
||||||
])
|
])
|
||||||
}
|
}
|
||||||
fn properties(&self, _user: &ArcUser, prop: dav::PropName<All>) -> Vec<dav::AnyProperty<All>> {
|
fn properties(&self, _user: &ArcUser, prop: dav::PropName<All>) -> PropertyStream<'static> {
|
||||||
prop.0.into_iter().map(|n| match n {
|
let calname = self.calname.to_string();
|
||||||
dav::PropertyRequest::DisplayName => dav::AnyProperty::Value(dav::Property::DisplayName(format!("{} calendar", self.calname))),
|
|
||||||
dav::PropertyRequest::ResourceType => dav::AnyProperty::Value(dav::Property::ResourceType(vec![
|
futures::stream::iter(prop.0).map(move |n| {
|
||||||
|
let prop = match n {
|
||||||
|
dav::PropertyRequest::DisplayName => dav::Property::DisplayName(format!("{} calendar", calname)),
|
||||||
|
dav::PropertyRequest::ResourceType => dav::Property::ResourceType(vec![
|
||||||
dav::ResourceType::Collection,
|
dav::ResourceType::Collection,
|
||||||
dav::ResourceType::Extension(all::ResourceType::Cal(cal::ResourceType::Calendar)),
|
dav::ResourceType::Extension(all::ResourceType::Cal(cal::ResourceType::Calendar)),
|
||||||
])),
|
]),
|
||||||
//dav::PropertyRequest::GetContentType => dav::AnyProperty::Value(dav::Property::GetContentType("httpd/unix-directory".into())),
|
//dav::PropertyRequest::GetContentType => dav::AnyProperty::Value(dav::Property::GetContentType("httpd/unix-directory".into())),
|
||||||
//@FIXME seems wrong but seems to be what Thunderbird expects...
|
//@FIXME seems wrong but seems to be what Thunderbird expects...
|
||||||
dav::PropertyRequest::GetContentType => dav::AnyProperty::Value(dav::Property::GetContentType("text/calendar".into())),
|
dav::PropertyRequest::GetContentType => dav::Property::GetContentType("text/calendar".into()),
|
||||||
dav::PropertyRequest::Extension(all::PropertyRequest::Cal(cal::PropertyRequest::SupportedCalendarComponentSet))
|
dav::PropertyRequest::Extension(all::PropertyRequest::Cal(cal::PropertyRequest::SupportedCalendarComponentSet))
|
||||||
=> dav::AnyProperty::Value(dav::Property::Extension(all::Property::Cal(cal::Property::SupportedCalendarComponentSet(vec![
|
=> dav::Property::Extension(all::Property::Cal(cal::Property::SupportedCalendarComponentSet(vec![
|
||||||
cal::CompSupport(cal::Component::VEvent),
|
cal::CompSupport(cal::Component::VEvent),
|
||||||
])))),
|
]))),
|
||||||
v => dav::AnyProperty::Request(v),
|
v => return Err(v),
|
||||||
}).collect()
|
};
|
||||||
|
Ok(prop)
|
||||||
|
}).boxed()
|
||||||
}
|
}
|
||||||
|
|
||||||
fn put<'a>(&'a self, _policy: PutPolicy, stream: Content<'a>) -> BoxFuture<'a, Result<Etag>> {
|
fn put<'a>(&'a self, _policy: PutPolicy, stream: Content<'a>) -> BoxFuture<'a, Result<Etag>> {
|
||||||
todo!()
|
todo!()
|
||||||
}
|
}
|
||||||
|
|
||||||
fn content<'a>(&'a self) -> BoxFuture<'a, Content<'static>> {
|
fn content<'a>(&'a self) -> Content<'static> {
|
||||||
async {
|
|
||||||
futures::stream::once(futures::future::err(std::io::Error::from(std::io::ErrorKind::Unsupported))).boxed()
|
futures::stream::once(futures::future::err(std::io::Error::from(std::io::ErrorKind::Unsupported))).boxed()
|
||||||
}.boxed()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn content_type(&self) -> &str {
|
fn content_type(&self) -> &str {
|
||||||
|
@ -333,37 +346,6 @@ impl DavNode for CalendarNode {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const FAKE_ICS: &str = r#"BEGIN:VCALENDAR
|
|
||||||
VERSION:2.0
|
|
||||||
PRODID:-//Example Corp.//CalDAV Client//EN
|
|
||||||
BEGIN:VTIMEZONE
|
|
||||||
LAST-MODIFIED:20040110T032845Z
|
|
||||||
TZID:US/Eastern
|
|
||||||
BEGIN:DAYLIGHT
|
|
||||||
DTSTART:20000404T020000
|
|
||||||
RRULE:FREQ=YEARLY;BYDAY=1SU;BYMONTH=4
|
|
||||||
TZNAME:EDT
|
|
||||||
TZOFFSETFROM:-0500
|
|
||||||
TZOFFSETTO:-0400
|
|
||||||
END:DAYLIGHT
|
|
||||||
BEGIN:STANDARD
|
|
||||||
DTSTART:20001026T020000
|
|
||||||
RRULE:FREQ=YEARLY;BYDAY=-1SU;BYMONTH=10
|
|
||||||
TZNAME:EST
|
|
||||||
TZOFFSETFROM:-0400
|
|
||||||
TZOFFSETTO:-0500
|
|
||||||
END:STANDARD
|
|
||||||
END:VTIMEZONE
|
|
||||||
BEGIN:VEVENT
|
|
||||||
DTSTAMP:20240406T001102Z
|
|
||||||
DTSTART;TZID=US/Eastern:20240406T100000
|
|
||||||
DURATION:PT1H
|
|
||||||
SUMMARY:Event #1
|
|
||||||
Description:Go Steelers!
|
|
||||||
UID:74855313FA803DA593CD579A@example.com
|
|
||||||
END:VEVENT
|
|
||||||
END:VCALENDAR"#;
|
|
||||||
|
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
pub(crate) struct EventNode {
|
pub(crate) struct EventNode {
|
||||||
col: Arc<Calendar>,
|
col: Arc<Calendar>,
|
||||||
|
@ -403,19 +385,31 @@ impl DavNode for EventNode {
|
||||||
dav::PropertyRequest::Extension(all::PropertyRequest::Cal(cal::PropertyRequest::CalendarData(cal::CalendarDataRequest::default()))),
|
dav::PropertyRequest::Extension(all::PropertyRequest::Cal(cal::PropertyRequest::CalendarData(cal::CalendarDataRequest::default()))),
|
||||||
])
|
])
|
||||||
}
|
}
|
||||||
fn properties(&self, _user: &ArcUser, prop: dav::PropName<All>) -> Vec<dav::AnyProperty<All>> {
|
fn properties(&self, _user: &ArcUser, prop: dav::PropName<All>) -> PropertyStream<'static> {
|
||||||
prop.0.into_iter().map(|n| match n {
|
let this = self.clone();
|
||||||
dav::PropertyRequest::DisplayName => dav::AnyProperty::Value(dav::Property::DisplayName(format!("{} event", self.filename))),
|
|
||||||
dav::PropertyRequest::ResourceType => dav::AnyProperty::Value(dav::Property::ResourceType(vec![])),
|
futures::stream::iter(prop.0).then(move |n| {
|
||||||
dav::PropertyRequest::GetContentType => dav::AnyProperty::Value(dav::Property::GetContentType("text/calendar".into())),
|
let this = this.clone();
|
||||||
dav::PropertyRequest::GetEtag => dav::AnyProperty::Value(dav::Property::GetEtag("\"abcdefg\"".into())),
|
|
||||||
dav::PropertyRequest::Extension(all::PropertyRequest::Cal(cal::PropertyRequest::CalendarData(_req))) =>
|
async move {
|
||||||
dav::AnyProperty::Value(dav::Property::Extension(all::Property::Cal(cal::Property::CalendarData(cal::CalendarDataPayload {
|
let prop = match &n {
|
||||||
|
dav::PropertyRequest::DisplayName => dav::Property::DisplayName(format!("{} event", this.filename)),
|
||||||
|
dav::PropertyRequest::ResourceType => dav::Property::ResourceType(vec![]),
|
||||||
|
dav::PropertyRequest::GetContentType => dav::Property::GetContentType("text/calendar".into()),
|
||||||
|
dav::PropertyRequest::GetEtag => dav::Property::GetEtag("\"abcdefg\"".into()),
|
||||||
|
dav::PropertyRequest::Extension(all::PropertyRequest::Cal(cal::PropertyRequest::CalendarData(_req))) => {
|
||||||
|
let ics = String::from_utf8(this.col.get(this.blob_id).await.or(Err(n.clone()))?).or(Err(n.clone()))?;
|
||||||
|
|
||||||
|
dav::Property::Extension(all::Property::Cal(cal::Property::CalendarData(cal::CalendarDataPayload {
|
||||||
mime: None,
|
mime: None,
|
||||||
payload: FAKE_ICS.into()
|
payload: ics,
|
||||||
})))),
|
})))
|
||||||
v => dav::AnyProperty::Request(v),
|
},
|
||||||
}).collect()
|
_ => return Err(n),
|
||||||
|
};
|
||||||
|
Ok(prop)
|
||||||
|
}
|
||||||
|
}).boxed()
|
||||||
}
|
}
|
||||||
|
|
||||||
fn put<'a>(&'a self, policy: PutPolicy, stream: Content<'a>) -> BoxFuture<'a, Result<Etag>> {
|
fn put<'a>(&'a self, policy: PutPolicy, stream: Content<'a>) -> BoxFuture<'a, Result<Etag>> {
|
||||||
|
@ -437,17 +431,16 @@ impl DavNode for EventNode {
|
||||||
}.boxed()
|
}.boxed()
|
||||||
}
|
}
|
||||||
|
|
||||||
fn content<'a>(&'a self) -> BoxFuture<'a, Content<'static>> {
|
fn content<'a>(&'a self) -> Content<'static> {
|
||||||
async {
|
|
||||||
//@FIXME for now, our storage interface does not allow streaming,
|
//@FIXME for now, our storage interface does not allow streaming,
|
||||||
// so we load everything in memory
|
// so we load everything in memory
|
||||||
let content = self.col.get(self.blob_id).await.or(Err(std::io::Error::from(std::io::ErrorKind::Interrupted)));
|
let calendar = self.col.clone();
|
||||||
let r = async {
|
let blob_id = self.blob_id.clone();
|
||||||
|
let r = async move {
|
||||||
|
let content = calendar.get(blob_id).await.or(Err(std::io::Error::from(std::io::ErrorKind::Interrupted)));
|
||||||
Ok(hyper::body::Bytes::from(content?))
|
Ok(hyper::body::Bytes::from(content?))
|
||||||
};
|
};
|
||||||
//tokio::pin!(r);
|
|
||||||
futures::stream::once(Box::pin(r)).boxed()
|
futures::stream::once(Box::pin(r)).boxed()
|
||||||
}.boxed()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn content_type(&self) -> &str {
|
fn content_type(&self) -> &str {
|
||||||
|
@ -483,8 +476,8 @@ impl DavNode for CreateEventNode {
|
||||||
dav::PropName(vec![])
|
dav::PropName(vec![])
|
||||||
}
|
}
|
||||||
|
|
||||||
fn properties(&self, _user: &ArcUser, prop: dav::PropName<All>) -> Vec<dav::AnyProperty<All>> {
|
fn properties(&self, _user: &ArcUser, prop: dav::PropName<All>) -> PropertyStream<'static> {
|
||||||
vec![]
|
futures::stream::iter(vec![]).boxed()
|
||||||
}
|
}
|
||||||
|
|
||||||
fn put<'a>(&'a self, _policy: PutPolicy, stream: Content<'a>) -> BoxFuture<'a, Result<Etag>> {
|
fn put<'a>(&'a self, _policy: PutPolicy, stream: Content<'a>) -> BoxFuture<'a, Result<Etag>> {
|
||||||
|
@ -500,10 +493,8 @@ impl DavNode for CreateEventNode {
|
||||||
}.boxed()
|
}.boxed()
|
||||||
}
|
}
|
||||||
|
|
||||||
fn content<'a>(&'a self) -> BoxFuture<'a, Content<'static>> {
|
fn content(&self) -> Content<'static> {
|
||||||
async {
|
|
||||||
futures::stream::once(futures::future::err(std::io::Error::from(std::io::ErrorKind::Unsupported))).boxed()
|
futures::stream::once(futures::future::err(std::io::Error::from(std::io::ErrorKind::Unsupported))).boxed()
|
||||||
}.boxed()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn content_type(&self) -> &str {
|
fn content_type(&self) -> &str {
|
||||||
|
|
Loading…
Reference in a new issue