296 lines
8.2 KiB
Rust
296 lines
8.2 KiB
Rust
/*
|
|
* SPDX-FileCopyrightText: 2020 Stalwart Labs LLC <hello@stalw.art>
|
|
*
|
|
* SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-SEL
|
|
*/
|
|
|
|
use super::{
|
|
Collation, MatchType,
|
|
property::{DavProperty, DavValue, LockScope, LockType},
|
|
response::Ace,
|
|
};
|
|
use crate::{Condition, Depth};
|
|
use calcard::{
|
|
icalendar::{ICalendarComponentType, ICalendarParameterName, ICalendarProperty},
|
|
vcard::{VCardParameterName, VCardProperty},
|
|
};
|
|
use types::{
|
|
TimeRange,
|
|
dead_property::{ArchivedDeadProperty, ArchivedDeadPropertyTag, DeadElementTag, DeadProperty},
|
|
};
|
|
|
|
#[derive(Debug, Clone, PartialEq, Eq, Default)]
|
|
#[cfg_attr(test, derive(serde::Serialize, serde::Deserialize))]
|
|
#[cfg_attr(test, serde(tag = "type", content = "data"))]
|
|
pub enum PropFind {
|
|
#[default]
|
|
PropName,
|
|
AllProp(Vec<DavProperty>),
|
|
Prop(Vec<DavProperty>),
|
|
}
|
|
|
|
#[derive(Debug, Clone, PartialEq, Eq)]
|
|
#[cfg_attr(test, derive(serde::Serialize, serde::Deserialize))]
|
|
pub struct PropertyUpdate {
|
|
pub set: Vec<DavPropertyValue>,
|
|
pub remove: Vec<DavProperty>,
|
|
pub set_first: bool,
|
|
}
|
|
|
|
#[derive(Debug, Clone, PartialEq, Eq)]
|
|
#[cfg_attr(test, derive(serde::Serialize, serde::Deserialize))]
|
|
pub struct DavPropertyValue {
|
|
pub property: DavProperty,
|
|
pub value: DavValue,
|
|
}
|
|
|
|
#[derive(Debug, Clone, PartialEq, Eq)]
|
|
#[cfg_attr(test, derive(serde::Serialize, serde::Deserialize))]
|
|
pub struct MkCol {
|
|
pub is_mkcalendar: bool,
|
|
pub props: Vec<DavPropertyValue>,
|
|
}
|
|
|
|
#[derive(Debug, Clone, PartialEq, Eq)]
|
|
#[cfg_attr(test, derive(serde::Serialize, serde::Deserialize))]
|
|
pub struct LockInfo {
|
|
pub lock_scope: LockScope,
|
|
pub lock_type: LockType,
|
|
pub owner: Option<DeadProperty>,
|
|
}
|
|
|
|
#[derive(Debug, Clone, PartialEq, Eq)]
|
|
#[cfg_attr(test, derive(serde::Serialize, serde::Deserialize))]
|
|
#[cfg_attr(test, serde(tag = "type"))]
|
|
pub enum Report {
|
|
AddressbookQuery(AddressbookQuery),
|
|
AddressbookMultiGet(MultiGet),
|
|
CalendarQuery(CalendarQuery),
|
|
CalendarMultiGet(MultiGet),
|
|
FreeBusyQuery(FreeBusyQuery),
|
|
SyncCollection(SyncCollection),
|
|
ExpandProperty(ExpandProperty),
|
|
AclPrincipalPropSet(AclPrincipalPropSet),
|
|
PrincipalMatch(PrincipalMatch),
|
|
PrincipalPropertySearch(PrincipalPropertySearch),
|
|
PrincipalSearchPropertySet,
|
|
}
|
|
|
|
#[derive(Debug, Clone, PartialEq, Eq)]
|
|
#[cfg_attr(test, derive(serde::Serialize, serde::Deserialize))]
|
|
pub struct ExpandProperty {
|
|
pub properties: Vec<ExpandPropertyItem>,
|
|
}
|
|
|
|
#[derive(Debug, Clone, PartialEq, Eq)]
|
|
#[cfg_attr(test, derive(serde::Serialize, serde::Deserialize))]
|
|
pub struct ExpandPropertyItem {
|
|
pub property: DavProperty,
|
|
pub depth: u32,
|
|
}
|
|
|
|
#[derive(Debug, Clone, PartialEq, Eq)]
|
|
#[cfg_attr(test, derive(serde::Serialize, serde::Deserialize))]
|
|
pub struct AddressbookQuery {
|
|
pub properties: PropFind,
|
|
pub filters: Vec<Filter<(), VCardPropertyWithGroup, VCardParameterName>>,
|
|
pub limit: Option<u32>,
|
|
}
|
|
|
|
#[derive(Debug, Clone, PartialEq, Eq)]
|
|
#[cfg_attr(test, derive(serde::Serialize, serde::Deserialize))]
|
|
pub struct VCardPropertyWithGroup {
|
|
pub name: VCardProperty,
|
|
pub group: Option<String>,
|
|
}
|
|
|
|
#[derive(Debug, Clone, PartialEq, Eq)]
|
|
#[cfg_attr(test, derive(serde::Serialize, serde::Deserialize))]
|
|
pub struct CalendarQuery {
|
|
pub properties: PropFind,
|
|
pub filters:
|
|
Vec<Filter<Vec<ICalendarComponentType>, ICalendarProperty, ICalendarParameterName>>,
|
|
pub timezone: Timezone,
|
|
}
|
|
|
|
#[derive(Debug, Clone, PartialEq, Eq)]
|
|
#[cfg_attr(test, derive(serde::Serialize, serde::Deserialize))]
|
|
#[cfg_attr(test, serde(tag = "type"))]
|
|
pub enum Timezone {
|
|
Name(String),
|
|
Id(String),
|
|
None,
|
|
}
|
|
|
|
#[derive(Debug, Clone, PartialEq, Eq)]
|
|
#[cfg_attr(test, derive(serde::Serialize, serde::Deserialize))]
|
|
pub struct FreeBusyQuery {
|
|
pub range: Option<TimeRange>,
|
|
}
|
|
|
|
#[derive(Debug, Clone, PartialEq, Eq)]
|
|
#[cfg_attr(test, derive(serde::Serialize, serde::Deserialize))]
|
|
pub struct MultiGet {
|
|
pub properties: PropFind,
|
|
pub hrefs: Vec<String>,
|
|
}
|
|
|
|
#[derive(Debug, Clone, PartialEq, Eq)]
|
|
#[cfg_attr(test, derive(serde::Serialize, serde::Deserialize))]
|
|
pub struct SyncCollection {
|
|
pub sync_token: Option<String>,
|
|
pub properties: PropFind,
|
|
pub depth: Depth,
|
|
pub limit: Option<u32>,
|
|
}
|
|
|
|
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
|
|
#[cfg_attr(test, derive(serde::Serialize, serde::Deserialize))]
|
|
#[cfg_attr(test, serde(tag = "type"))]
|
|
pub enum Filter<A, B, C> {
|
|
AnyOf,
|
|
AllOf,
|
|
Component {
|
|
comp: A,
|
|
op: FilterOp,
|
|
},
|
|
Property {
|
|
comp: A,
|
|
prop: B,
|
|
op: FilterOp,
|
|
},
|
|
Parameter {
|
|
comp: A,
|
|
prop: B,
|
|
param: C,
|
|
op: FilterOp,
|
|
},
|
|
}
|
|
|
|
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
|
|
#[cfg_attr(test, derive(serde::Serialize, serde::Deserialize))]
|
|
#[cfg_attr(test, serde(tag = "type", content = "data"))]
|
|
pub enum FilterOp {
|
|
Exists,
|
|
Undefined,
|
|
TimeRange(TimeRange),
|
|
TextMatch(TextMatch),
|
|
}
|
|
|
|
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
|
|
#[cfg_attr(test, derive(serde::Serialize, serde::Deserialize))]
|
|
#[cfg_attr(test, serde(tag = "type"))]
|
|
pub struct TextMatch {
|
|
pub match_type: MatchType,
|
|
pub value: String,
|
|
pub collation: Collation,
|
|
pub negate: bool,
|
|
}
|
|
|
|
#[derive(Debug, Clone, PartialEq, Eq)]
|
|
#[cfg_attr(test, derive(serde::Serialize, serde::Deserialize))]
|
|
pub struct Acl {
|
|
pub aces: Vec<Ace>,
|
|
}
|
|
|
|
#[derive(Debug, Clone, PartialEq, Eq)]
|
|
#[cfg_attr(test, derive(serde::Serialize, serde::Deserialize))]
|
|
pub struct AclPrincipalPropSet {
|
|
pub properties: Vec<DavProperty>,
|
|
}
|
|
|
|
#[derive(Debug, Clone, PartialEq, Eq)]
|
|
#[cfg_attr(test, derive(serde::Serialize, serde::Deserialize))]
|
|
pub struct PrincipalMatch {
|
|
pub principal_properties: PrincipalMatchProperties,
|
|
pub properties: Vec<DavProperty>,
|
|
}
|
|
|
|
#[derive(Debug, Clone, PartialEq, Eq)]
|
|
#[cfg_attr(test, derive(serde::Serialize, serde::Deserialize))]
|
|
pub enum PrincipalMatchProperties {
|
|
Properties(Vec<DavProperty>),
|
|
Self_,
|
|
}
|
|
|
|
#[derive(Debug, Clone, PartialEq, Eq)]
|
|
#[cfg_attr(test, derive(serde::Serialize, serde::Deserialize))]
|
|
pub struct PrincipalPropertySearch {
|
|
pub property_search: Vec<PropertySearch>,
|
|
pub properties: Vec<DavProperty>,
|
|
pub apply_to_principal_collection_set: bool,
|
|
}
|
|
|
|
#[derive(Debug, Clone, PartialEq, Eq)]
|
|
#[cfg_attr(test, derive(serde::Serialize, serde::Deserialize))]
|
|
pub struct PropertySearch {
|
|
pub property: DavProperty,
|
|
pub match_: String,
|
|
}
|
|
|
|
impl PropertyUpdate {
|
|
pub fn has_changes(&self) -> bool {
|
|
!self.set.is_empty() || !self.remove.is_empty()
|
|
}
|
|
}
|
|
|
|
impl FreeBusyQuery {
|
|
pub fn new(start: i64, end: i64) -> Self {
|
|
FreeBusyQuery {
|
|
range: Some(TimeRange { start, end }),
|
|
}
|
|
}
|
|
}
|
|
|
|
pub trait DavDeadProperty {
|
|
fn to_dav_values(&self, output: &mut Vec<DavPropertyValue>);
|
|
}
|
|
|
|
impl DavDeadProperty for ArchivedDeadProperty {
|
|
fn to_dav_values(&self, output: &mut Vec<DavPropertyValue>) {
|
|
let mut depth: u32 = 0;
|
|
let mut tags = Vec::new();
|
|
let mut tag_start = None;
|
|
|
|
for tag in self.0.iter() {
|
|
match tag {
|
|
ArchivedDeadPropertyTag::ElementStart(start) => {
|
|
if depth == 0 {
|
|
tag_start = Some(DeadElementTag::from(start));
|
|
} else {
|
|
tags.push(tag.into());
|
|
}
|
|
|
|
depth += 1;
|
|
}
|
|
ArchivedDeadPropertyTag::ElementEnd => {
|
|
depth = depth.saturating_sub(1);
|
|
|
|
if depth > 0 {
|
|
tags.push(tag.into());
|
|
} else if let Some(tag_start) = tag_start.take() {
|
|
output.push(DavPropertyValue::new(
|
|
DavProperty::DeadProperty(tag_start),
|
|
DavValue::DeadProperty(DeadProperty(std::mem::take(&mut tags))),
|
|
));
|
|
}
|
|
}
|
|
ArchivedDeadPropertyTag::Text(_) => {
|
|
if tag_start.is_some() {
|
|
tags.push(tag.into());
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
impl Condition<'_> {
|
|
pub fn is_none_match(&self) -> bool {
|
|
match self {
|
|
Condition::ETag { is_not, .. } | Condition::Exists { is_not } => *is_not,
|
|
Condition::StateToken { .. } => false,
|
|
}
|
|
}
|
|
}
|