/* * SPDX-FileCopyrightText: 2020 Stalwart Labs LLC * * SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-SEL */ use super::{XmlCdataEscape, XmlEscape}; use crate::{ responses::DeadPropertyFormat, schema::{ Namespace, Namespaces, property::{ ActiveLock, CalDavProperty, CardDavProperty, Comp, DavProperty, DavValue, LockDiscovery, LockEntry, PrincipalProperty, Privilege, ReportSet, ResourceType, Rfc1123DateTime, SupportedCollation, SupportedLock, WebDavProperty, }, request::DavPropertyValue, response::{Ace, AclRestrictions, Href, List, PropResponse, SupportedPrivilege}, }, }; use calcard::icalendar::ICalendarComponentType; use mail_parser::{ DateTime, parsers::fields::date::{DOW, MONTH}, }; use std::fmt::Display; use types::dead_property::DeadProperty; impl Display for PropResponse { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { write!( f, "{}", self.namespaces, self.properties ) } } impl Display for DavPropertyValue { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { let (name, attrs) = self.property.tag_name(); write!(f, "<{}", name)?; if let Some(attrs) = attrs { write!(f, " {attrs}")?; } if !matches!(self.value, DavValue::Null) { write!(f, ">{}", self.value, name) } else { write!(f, "/>") } } } impl Display for Rfc1123DateTime { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { let dt = DateTime::from_timestamp(self.0); write!( f, "{}, {:02} {} {:04} {:02}:{:02}:{:02} GMT", DOW[dt.day_of_week() as usize], dt.day, MONTH .get(dt.month.saturating_sub(1) as usize) .copied() .unwrap_or_default(), dt.year, dt.hour, dt.minute, dt.second, ) } } impl Display for DavValue { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { match self { DavValue::Timestamp(v) => { let dt = DateTime::from_timestamp(*v); write!( f, "{:04}-{:02}-{:02}T{:02}:{:02}:{:02}Z", dt.year, dt.month, dt.day, dt.hour, dt.minute, dt.second, ) } DavValue::Rfc1123Date(v) => v.fmt(f), DavValue::Uint64(v) => v.fmt(f), DavValue::String(v) => v.write_escaped_to(f), DavValue::ResourceTypes(v) => v.fmt(f), DavValue::ActiveLocks(v) => v.fmt(f), DavValue::LockEntries(v) => v.fmt(f), DavValue::ReportSets(v) => v.fmt(f), DavValue::CData(v) => v.write_cdata_escaped_to(f), DavValue::Components(v) => v.fmt(f), DavValue::Collations(v) => v.fmt(f), DavValue::Href(v) => v.fmt(f), DavValue::PrivilegeSet(v) => v.fmt(f), DavValue::Privileges(v) => v.fmt(f), DavValue::Acl(v) => v.fmt(f), DavValue::AclRestrictions(v) => v.fmt(f), DavValue::DeadProperty(v) => v.fmt(f), DavValue::SupportedAddressData => { write!( f, concat!( "", "", "", ) ) } DavValue::SupportedCalendarData => { write!( f, concat!( "", "", ) ) } DavValue::Response(v) => v.fmt(f), DavValue::VCard(_) | DavValue::ICalendar(_) | DavValue::Null => Ok(()), } } } impl DavValue { pub fn all_calendar_components() -> Self { DavValue::Components(List(vec![ Comp(ICalendarComponentType::VEvent), Comp(ICalendarComponentType::VTodo), Comp(ICalendarComponentType::VJournal), Comp(ICalendarComponentType::VFreebusy), Comp(ICalendarComponentType::VTimezone), Comp(ICalendarComponentType::VAlarm), Comp(ICalendarComponentType::Standard), Comp(ICalendarComponentType::Daylight), Comp(ICalendarComponentType::VAvailability), Comp(ICalendarComponentType::Available), Comp(ICalendarComponentType::Participant), Comp(ICalendarComponentType::VLocation), Comp(ICalendarComponentType::VResource), ])) } } impl DavProperty { fn tag_name(&self) -> (&str, Option<&str>) { ( match self { DavProperty::WebDav(prop) => match prop { WebDavProperty::CreationDate => "D:creationdate", WebDavProperty::DisplayName => "D:displayname", WebDavProperty::GetContentLanguage => "D:getcontentlanguage", WebDavProperty::GetContentLength => "D:getcontentlength", WebDavProperty::GetContentType => "D:getcontenttype", WebDavProperty::GetETag => "D:getetag", WebDavProperty::GetLastModified => "D:getlastmodified", WebDavProperty::ResourceType => "D:resourcetype", WebDavProperty::LockDiscovery => "D:lockdiscovery", WebDavProperty::SupportedLock => "D:supportedlock", WebDavProperty::CurrentUserPrincipal => "D:current-user-principal", WebDavProperty::QuotaAvailableBytes => "D:quota-available-bytes", WebDavProperty::QuotaUsedBytes => "D:quota-used-bytes", WebDavProperty::SupportedReportSet => "D:supported-report-set", WebDavProperty::SyncToken => "D:sync-token", WebDavProperty::Owner => "D:owner", WebDavProperty::Group => "D:group", WebDavProperty::SupportedPrivilegeSet => "D:supported-privilege-set", WebDavProperty::CurrentUserPrivilegeSet => "D:current-user-privilege-set", WebDavProperty::Acl => "D:acl", WebDavProperty::AclRestrictions => "D:acl-restrictions", WebDavProperty::InheritedAclSet => "D:inherited-acl-set", WebDavProperty::PrincipalCollectionSet => "D:principal-collection-set", WebDavProperty::GetCTag => "C:getctag", }, DavProperty::CardDav(prop) => match prop { CardDavProperty::AddressbookDescription => "B:addressbook-description", CardDavProperty::SupportedAddressData => "B:supported-address-data", CardDavProperty::SupportedCollationSet => "B:supported-collation-set", CardDavProperty::MaxResourceSize => "B:max-resource-size", CardDavProperty::AddressData { .. } => "B:address-data", }, DavProperty::CalDav(prop) => match prop { CalDavProperty::CalendarDescription => "A:calendar-description", CalDavProperty::CalendarTimezone => "A:calendar-timezone", CalDavProperty::SupportedCalendarComponentSet => { "A:supported-calendar-component-set" } CalDavProperty::SupportedCalendarData => "A:supported-calendar-data", CalDavProperty::SupportedCollationSet => "A:supported-collation-set", CalDavProperty::MaxResourceSize => "A:max-resource-size", CalDavProperty::MinDateTime => "A:min-date-time", CalDavProperty::MaxDateTime => "A:max-date-time", CalDavProperty::MaxInstances => "A:max-instances", CalDavProperty::MaxAttendeesPerInstance => "A:max-attendees-per-instance", CalDavProperty::CalendarData(_) => "A:calendar-data", CalDavProperty::TimezoneServiceSet => "A:timezone-service-set", CalDavProperty::TimezoneId => "A:calendar-timezone-id", CalDavProperty::ScheduleDefaultCalendarURL => "A:schedule-default-calendar-URL", CalDavProperty::ScheduleTag => "A:schedule-tag", CalDavProperty::ScheduleCalendarTransp => "A:schedule-calendar-transp", }, DavProperty::Principal(prop) => match prop { PrincipalProperty::AlternateURISet => "D:alternate-URI-set", PrincipalProperty::PrincipalURL => "D:principal-URL", PrincipalProperty::GroupMemberSet => "D:group-member-set", PrincipalProperty::GroupMembership => "D:group-membership", PrincipalProperty::CalendarHomeSet => "A:calendar-home-set", PrincipalProperty::AddressbookHomeSet => "B:addressbook-home-set", PrincipalProperty::PrincipalAddress => "B:principal-address", PrincipalProperty::CalendarUserAddressSet => "A:calendar-user-address-set", PrincipalProperty::CalendarUserType => "A:calendar-user-type", PrincipalProperty::ScheduleInboxURL => "A:schedule-inbox-URL", PrincipalProperty::ScheduleOutboxURL => "A:schedule-outbox-URL", }, DavProperty::DeadProperty(dead) => { return (dead.name.as_str(), dead.attrs.as_deref()); } }, None, ) } pub fn namespace(&self) -> Namespace { match self { DavProperty::WebDav(WebDavProperty::GetCTag) => Namespace::CalendarServer, DavProperty::CardDav(_) | DavProperty::Principal( PrincipalProperty::AddressbookHomeSet | PrincipalProperty::PrincipalAddress, ) => Namespace::CardDav, DavProperty::CalDav(_) | DavProperty::Principal( PrincipalProperty::CalendarHomeSet | PrincipalProperty::CalendarUserAddressSet | PrincipalProperty::CalendarUserType | PrincipalProperty::ScheduleInboxURL | PrincipalProperty::ScheduleOutboxURL, ) => Namespace::CalDav, _ => Namespace::Dav, } } } impl AsRef for DavProperty { fn as_ref(&self) -> &str { self.tag_name().0 } } impl Display for ReportSet { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { f.write_str("")?; match self { ReportSet::SyncCollection => write!(f, ""), ReportSet::ExpandProperty => write!(f, ""), ReportSet::AddressbookQuery => write!(f, ""), ReportSet::AddressbookMultiGet => write!(f, ""), ReportSet::CalendarQuery => write!(f, ""), ReportSet::CalendarMultiGet => write!(f, ""), ReportSet::FreeBusyQuery => write!(f, ""), ReportSet::AclPrincipalPropSet => write!(f, ""), ReportSet::PrincipalMatch => write!(f, ""), ReportSet::PrincipalPropertySearch => write!(f, ""), ReportSet::PrincipalSearchPropertySet => { write!(f, "") } }?; f.write_str("") } } impl Display for DavProperty { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { let (name, attrs) = self.tag_name(); if let Some(attrs) = attrs { write!(f, "<{name} {attrs}/>") } else { write!(f, "<{name}/>") } } } impl PropResponse { pub fn new(properties: Vec) -> Self { PropResponse { namespaces: Namespaces::default(), properties: List(properties), } } pub fn with_namespace(mut self, namespace: Namespace) -> Self { self.namespaces.set(namespace); self } } impl From for DavProperty { fn from(prop: WebDavProperty) -> Self { DavProperty::WebDav(prop) } } impl From for DavProperty { fn from(prop: CardDavProperty) -> Self { DavProperty::CardDav(prop) } } impl From for DavProperty { fn from(prop: CalDavProperty) -> Self { DavProperty::CalDav(prop) } } impl From for DavValue { fn from(v: String) -> Self { DavValue::String(v) } } impl From<&str> for DavValue { fn from(v: &str) -> Self { DavValue::String(v.to_string()) } } impl From for DavValue { fn from(v: u64) -> Self { DavValue::Uint64(v) } } impl From for DavValue { fn from(v: DateTime) -> Self { DavValue::Timestamp(v.to_timestamp()) } } impl From> for DavValue { fn from(v: Vec) -> Self { DavValue::ResourceTypes(List(v)) } } impl From> for DavValue { fn from(v: Vec) -> Self { DavValue::ReportSets(List(v)) } } impl From> for DavValue { fn from(v: Vec) -> Self { DavValue::Components(List(v)) } } impl From> for DavValue { fn from(v: Vec) -> Self { DavValue::Collations(List(v)) } } impl From for DavValue { fn from(v: SupportedLock) -> Self { DavValue::LockEntries(v.0) } } impl From> for DavValue { fn from(v: Vec) -> Self { DavValue::LockEntries(List(v)) } } impl From> for DavValue { fn from(v: Vec) -> Self { DavValue::ActiveLocks(List(v)) } } impl From for DavValue { fn from(v: LockDiscovery) -> Self { DavValue::ActiveLocks(v.0) } } impl From> for DavValue { fn from(v: Vec) -> Self { DavValue::PrivilegeSet(List(v)) } } impl From> for DavValue { fn from(v: Vec) -> Self { DavValue::Privileges(List(v)) } } impl From> for DavValue { fn from(v: Vec) -> Self { DavValue::Href(List(v)) } } impl From> for DavValue { fn from(v: Vec) -> Self { DavValue::Acl(List(v)) } } impl From for DavValue { fn from(v: AclRestrictions) -> Self { DavValue::AclRestrictions(v) } } impl From for DavValue { fn from(v: DeadProperty) -> Self { DavValue::DeadProperty(v) } } impl DavPropertyValue { pub fn new(property: impl Into, value: impl Into) -> Self { DavPropertyValue { property: property.into(), value: value.into(), } } pub fn empty(property: impl Into) -> Self { DavPropertyValue { property: property.into(), value: DavValue::Null, } } }