mirror of
https://github.com/lennart-k/rustical.git
synced 2025-12-13 18:12:27 +00:00
Fix bug with missing trailing slash in propfind response
This commit is contained in:
@@ -67,7 +67,7 @@ fn objects_response(
|
|||||||
object,
|
object,
|
||||||
principal: principal.to_owned(),
|
principal: principal.to_owned(),
|
||||||
}
|
}
|
||||||
.propfind_typed(&path, prop, puri, user)?,
|
.propfind(&path, prop, puri, user)?,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -39,7 +39,7 @@ pub async fn handle_sync_collection<C: CalendarStore>(
|
|||||||
object,
|
object,
|
||||||
principal: principal.to_owned(),
|
principal: principal.to_owned(),
|
||||||
}
|
}
|
||||||
.propfind_typed(&path, &sync_collection.prop, puri, user)?,
|
.propfind(&path, &sync_collection.prop, puri, user)?,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -100,6 +100,8 @@ impl Resource for CalendarResource {
|
|||||||
type Error = Error;
|
type Error = Error;
|
||||||
type Principal = User;
|
type Principal = User;
|
||||||
|
|
||||||
|
const IS_COLLECTION: bool = true;
|
||||||
|
|
||||||
fn get_resourcetype(&self) -> Resourcetype {
|
fn get_resourcetype(&self) -> Resourcetype {
|
||||||
if self.cal.subscription_url.is_none() {
|
if self.cal.subscription_url.is_none() {
|
||||||
Resourcetype(&[
|
Resourcetype(&[
|
||||||
|
|||||||
@@ -50,7 +50,6 @@ impl<C: CalendarStore, S: SubscriptionStore> ResourceService for CalendarResourc
|
|||||||
type PrincipalUri = CalDavPrincipalUri;
|
type PrincipalUri = CalDavPrincipalUri;
|
||||||
|
|
||||||
const DAV_HEADER: &str = "1, 3, access-control, calendar-access";
|
const DAV_HEADER: &str = "1, 3, access-control, calendar-access";
|
||||||
const IS_COLLECTION: bool = true;
|
|
||||||
|
|
||||||
async fn get_resource(
|
async fn get_resource(
|
||||||
&self,
|
&self,
|
||||||
|
|||||||
@@ -27,6 +27,8 @@ impl Resource for CalendarObjectResource {
|
|||||||
type Error = Error;
|
type Error = Error;
|
||||||
type Principal = User;
|
type Principal = User;
|
||||||
|
|
||||||
|
const IS_COLLECTION: bool = false;
|
||||||
|
|
||||||
fn get_resourcetype(&self) -> Resourcetype {
|
fn get_resourcetype(&self) -> Resourcetype {
|
||||||
Resourcetype(&[])
|
Resourcetype(&[])
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -50,7 +50,6 @@ impl<C: CalendarStore> ResourceService for CalendarObjectResourceService<C> {
|
|||||||
type PrincipalUri = CalDavPrincipalUri;
|
type PrincipalUri = CalDavPrincipalUri;
|
||||||
|
|
||||||
const DAV_HEADER: &str = "1, 3, access-control, calendar-access";
|
const DAV_HEADER: &str = "1, 3, access-control, calendar-access";
|
||||||
const IS_COLLECTION: bool = false;
|
|
||||||
|
|
||||||
async fn get_resource(
|
async fn get_resource(
|
||||||
&self,
|
&self,
|
||||||
|
|||||||
@@ -28,6 +28,8 @@ impl Resource for CalendarSetResource {
|
|||||||
type Error = Error;
|
type Error = Error;
|
||||||
type Principal = User;
|
type Principal = User;
|
||||||
|
|
||||||
|
const IS_COLLECTION: bool = true;
|
||||||
|
|
||||||
fn get_resourcetype(&self) -> Resourcetype {
|
fn get_resourcetype(&self) -> Resourcetype {
|
||||||
Resourcetype(&[ResourcetypeInner(
|
Resourcetype(&[ResourcetypeInner(
|
||||||
Some(rustical_dav::namespace::NS_DAV),
|
Some(rustical_dav::namespace::NS_DAV),
|
||||||
|
|||||||
@@ -45,7 +45,6 @@ impl<C: CalendarStore, S: SubscriptionStore> ResourceService for CalendarSetReso
|
|||||||
type PrincipalUri = CalDavPrincipalUri;
|
type PrincipalUri = CalDavPrincipalUri;
|
||||||
|
|
||||||
const DAV_HEADER: &str = "1, 3, access-control, extended-mkcol, calendar-access";
|
const DAV_HEADER: &str = "1, 3, access-control, extended-mkcol, calendar-access";
|
||||||
const IS_COLLECTION: bool = true;
|
|
||||||
|
|
||||||
async fn get_resource(
|
async fn get_resource(
|
||||||
&self,
|
&self,
|
||||||
|
|||||||
@@ -27,6 +27,8 @@ impl Resource for PrincipalResource {
|
|||||||
type Error = Error;
|
type Error = Error;
|
||||||
type Principal = User;
|
type Principal = User;
|
||||||
|
|
||||||
|
const IS_COLLECTION: bool = true;
|
||||||
|
|
||||||
fn get_resourcetype(&self) -> Resourcetype {
|
fn get_resourcetype(&self) -> Resourcetype {
|
||||||
Resourcetype(&[
|
Resourcetype(&[
|
||||||
ResourcetypeInner(Some(rustical_dav::namespace::NS_DAV), "collection"),
|
ResourcetypeInner(Some(rustical_dav::namespace::NS_DAV), "collection"),
|
||||||
|
|||||||
@@ -46,7 +46,6 @@ impl<AP: AuthenticationProvider, S: SubscriptionStore, CS: CalendarStore, BS: Ca
|
|||||||
type PrincipalUri = CalDavPrincipalUri;
|
type PrincipalUri = CalDavPrincipalUri;
|
||||||
|
|
||||||
const DAV_HEADER: &str = "1, 3, access-control, calendar-access";
|
const DAV_HEADER: &str = "1, 3, access-control, calendar-access";
|
||||||
const IS_COLLECTION: bool = true;
|
|
||||||
|
|
||||||
async fn get_resource(
|
async fn get_resource(
|
||||||
&self,
|
&self,
|
||||||
|
|||||||
@@ -32,6 +32,8 @@ impl Resource for AddressObjectResource {
|
|||||||
type Error = Error;
|
type Error = Error;
|
||||||
type Principal = User;
|
type Principal = User;
|
||||||
|
|
||||||
|
const IS_COLLECTION: bool = false;
|
||||||
|
|
||||||
fn get_resourcetype(&self) -> Resourcetype {
|
fn get_resourcetype(&self) -> Resourcetype {
|
||||||
Resourcetype(&[])
|
Resourcetype(&[])
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -41,7 +41,6 @@ impl<AS: AddressbookStore> ResourceService for AddressObjectResourceService<AS>
|
|||||||
type PrincipalUri = CardDavPrincipalUri;
|
type PrincipalUri = CardDavPrincipalUri;
|
||||||
|
|
||||||
const DAV_HEADER: &str = "1, 3, access-control, addressbook";
|
const DAV_HEADER: &str = "1, 3, access-control, addressbook";
|
||||||
const IS_COLLECTION: bool = false;
|
|
||||||
|
|
||||||
async fn get_resource(
|
async fn get_resource(
|
||||||
&self,
|
&self,
|
||||||
|
|||||||
@@ -80,7 +80,7 @@ pub async fn handle_addressbook_multiget<AS: AddressbookStore>(
|
|||||||
object,
|
object,
|
||||||
principal: principal.to_owned(),
|
principal: principal.to_owned(),
|
||||||
}
|
}
|
||||||
.propfind_typed(&path, prop, puri, user)?,
|
.propfind(&path, prop, puri, user)?,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -39,7 +39,7 @@ pub async fn handle_sync_collection<AS: AddressbookStore>(
|
|||||||
object,
|
object,
|
||||||
principal: principal.to_owned(),
|
principal: principal.to_owned(),
|
||||||
}
|
}
|
||||||
.propfind_typed(&path, &sync_collection.prop, puri, user)?,
|
.propfind(&path, &sync_collection.prop, puri, user)?,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -38,6 +38,8 @@ impl Resource for AddressbookResource {
|
|||||||
type Error = Error;
|
type Error = Error;
|
||||||
type Principal = User;
|
type Principal = User;
|
||||||
|
|
||||||
|
const IS_COLLECTION: bool = true;
|
||||||
|
|
||||||
fn get_resourcetype(&self) -> Resourcetype {
|
fn get_resourcetype(&self) -> Resourcetype {
|
||||||
Resourcetype(&[
|
Resourcetype(&[
|
||||||
ResourcetypeInner(Some(rustical_dav::namespace::NS_DAV), "collection"),
|
ResourcetypeInner(Some(rustical_dav::namespace::NS_DAV), "collection"),
|
||||||
|
|||||||
@@ -52,7 +52,6 @@ impl<AS: AddressbookStore, S: SubscriptionStore> ResourceService
|
|||||||
type PrincipalUri = CardDavPrincipalUri;
|
type PrincipalUri = CardDavPrincipalUri;
|
||||||
|
|
||||||
const DAV_HEADER: &str = "1, 3, access-control, addressbook";
|
const DAV_HEADER: &str = "1, 3, access-control, addressbook";
|
||||||
const IS_COLLECTION: bool = true;
|
|
||||||
|
|
||||||
async fn get_resource(
|
async fn get_resource(
|
||||||
&self,
|
&self,
|
||||||
|
|||||||
@@ -26,6 +26,8 @@ impl Resource for PrincipalResource {
|
|||||||
type Error = Error;
|
type Error = Error;
|
||||||
type Principal = User;
|
type Principal = User;
|
||||||
|
|
||||||
|
const IS_COLLECTION: bool = true;
|
||||||
|
|
||||||
fn get_resourcetype(&self) -> Resourcetype {
|
fn get_resourcetype(&self) -> Resourcetype {
|
||||||
Resourcetype(&[
|
Resourcetype(&[
|
||||||
ResourcetypeInner(Some(rustical_dav::namespace::NS_DAV), "collection"),
|
ResourcetypeInner(Some(rustical_dav::namespace::NS_DAV), "collection"),
|
||||||
|
|||||||
@@ -55,7 +55,6 @@ impl<A: AddressbookStore, AP: AuthenticationProvider, S: SubscriptionStore> Reso
|
|||||||
type PrincipalUri = CardDavPrincipalUri;
|
type PrincipalUri = CardDavPrincipalUri;
|
||||||
|
|
||||||
const DAV_HEADER: &str = "1, 3, access-control, addressbook";
|
const DAV_HEADER: &str = "1, 3, access-control, addressbook";
|
||||||
const IS_COLLECTION: bool = true;
|
|
||||||
|
|
||||||
async fn get_resource(
|
async fn get_resource(
|
||||||
&self,
|
&self,
|
||||||
|
|||||||
@@ -76,13 +76,8 @@ pub(crate) async fn route_propfind<R: ResourceService>(
|
|||||||
let mut member_responses = Vec::new();
|
let mut member_responses = Vec::new();
|
||||||
if depth != &Depth::Zero {
|
if depth != &Depth::Zero {
|
||||||
for member in resource_service.get_members(path_components).await? {
|
for member in resource_service.get_members(path_components).await? {
|
||||||
// Collections should have a trailing slash
|
member_responses.push(member.propfind(
|
||||||
let mut name = member.get_name();
|
&format!("{}/{}", path.trim_end_matches('/'), member.get_name()),
|
||||||
if R::IS_COLLECTION {
|
|
||||||
name.push('/')
|
|
||||||
}
|
|
||||||
member_responses.push(member.propfind_typed(
|
|
||||||
&format!("{}/{}", path.trim_end_matches('/'), name),
|
|
||||||
&propfind_member.prop,
|
&propfind_member.prop,
|
||||||
puri,
|
puri,
|
||||||
principal,
|
principal,
|
||||||
@@ -90,7 +85,7 @@ pub(crate) async fn route_propfind<R: ResourceService>(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
let response = resource.propfind_typed(path, &propfind_self.prop, puri, principal)?;
|
let response = resource.propfind(path, &propfind_self.prop, puri, principal)?;
|
||||||
|
|
||||||
Ok(MultistatusElement {
|
Ok(MultistatusElement {
|
||||||
responses: vec![response],
|
responses: vec![response],
|
||||||
|
|||||||
@@ -37,6 +37,8 @@ pub trait Resource: Clone + Send + 'static {
|
|||||||
type Error: From<crate::Error>;
|
type Error: From<crate::Error>;
|
||||||
type Principal: Principal;
|
type Principal: Principal;
|
||||||
|
|
||||||
|
const IS_COLLECTION: bool;
|
||||||
|
|
||||||
fn get_resourcetype(&self) -> Resourcetype;
|
fn get_resourcetype(&self) -> Resourcetype;
|
||||||
|
|
||||||
fn list_props() -> Vec<(Option<Namespace<'static>>, &'static str)> {
|
fn list_props() -> Vec<(Option<Namespace<'static>>, &'static str)> {
|
||||||
@@ -95,13 +97,19 @@ pub trait Resource: Clone + Send + 'static {
|
|||||||
principal: &Self::Principal,
|
principal: &Self::Principal,
|
||||||
) -> Result<UserPrivilegeSet, Self::Error>;
|
) -> Result<UserPrivilegeSet, Self::Error>;
|
||||||
|
|
||||||
fn propfind_typed(
|
fn propfind(
|
||||||
&self,
|
&self,
|
||||||
path: &str,
|
path: &str,
|
||||||
prop: &PropfindType<<Self::Prop as PropName>::Names>,
|
prop: &PropfindType<<Self::Prop as PropName>::Names>,
|
||||||
principal_uri: &impl PrincipalUri,
|
principal_uri: &impl PrincipalUri,
|
||||||
principal: &Self::Principal,
|
principal: &Self::Principal,
|
||||||
) -> Result<ResponseElement<Self::Prop>, Self::Error> {
|
) -> Result<ResponseElement<Self::Prop>, Self::Error> {
|
||||||
|
// Collections have a trailing slash
|
||||||
|
let mut path = path.to_string();
|
||||||
|
if Self::IS_COLLECTION && !path.ends_with('/') {
|
||||||
|
path.push('/');
|
||||||
|
}
|
||||||
|
|
||||||
// TODO: Support include element
|
// TODO: Support include element
|
||||||
let (props, invalid_props): (HashSet<<Self::Prop as PropName>::Names>, Vec<_>) = match prop
|
let (props, invalid_props): (HashSet<<Self::Prop as PropName>::Names>, Vec<_>) = match prop
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -18,7 +18,6 @@ pub trait ResourceService: Clone + Sized + Send + Sync + AxumMethods + 'static {
|
|||||||
type PrincipalUri: PrincipalUri;
|
type PrincipalUri: PrincipalUri;
|
||||||
|
|
||||||
const DAV_HEADER: &'static str;
|
const DAV_HEADER: &'static str;
|
||||||
const IS_COLLECTION: bool;
|
|
||||||
|
|
||||||
async fn get_members(
|
async fn get_members(
|
||||||
&self,
|
&self,
|
||||||
|
|||||||
@@ -24,6 +24,8 @@ impl<PR: Resource, P: Principal> Resource for RootResource<PR, P> {
|
|||||||
type Error = PR::Error;
|
type Error = PR::Error;
|
||||||
type Principal = P;
|
type Principal = P;
|
||||||
|
|
||||||
|
const IS_COLLECTION: bool = true;
|
||||||
|
|
||||||
fn get_resourcetype(&self) -> Resourcetype {
|
fn get_resourcetype(&self) -> Resourcetype {
|
||||||
Resourcetype(&[ResourcetypeInner(
|
Resourcetype(&[ResourcetypeInner(
|
||||||
Some(crate::namespace::NS_DAV),
|
Some(crate::namespace::NS_DAV),
|
||||||
@@ -77,7 +79,6 @@ where
|
|||||||
type PrincipalUri = PURI;
|
type PrincipalUri = PURI;
|
||||||
|
|
||||||
const DAV_HEADER: &str = "1, 3, access-control";
|
const DAV_HEADER: &str = "1, 3, access-control";
|
||||||
const IS_COLLECTION: bool = true;
|
|
||||||
|
|
||||||
async fn get_resource(&self, _: &()) -> Result<Self::Resource, Self::Error> {
|
async fn get_resource(&self, _: &()) -> Result<Self::Resource, Self::Error> {
|
||||||
Ok(RootResource::<PRS::Resource, P>::default())
|
Ok(RootResource::<PRS::Resource, P>::default())
|
||||||
|
|||||||
Reference in New Issue
Block a user