aboutsummaryrefslogtreecommitdiff
path: root/aero-proto
diff options
context:
space:
mode:
authorQuentin Dufour <quentin@deuxfleurs.fr>2024-05-25 19:30:59 +0200
committerQuentin Dufour <quentin@deuxfleurs.fr>2024-05-25 19:30:59 +0200
commit52f870633c2cab8a4aeeec74792774931139b8b5 (patch)
tree878d4ff16cdebd7fdfc50a278dbadedd7eb63480 /aero-proto
parentff823a10f049e06c711537560ba10f3dc826afcd (diff)
downloadaerogramme-52f870633c2cab8a4aeeec74792774931139b8b5.tar.gz
aerogramme-52f870633c2cab8a4aeeec74792774931139b8b5.zip
add a new aero-ical module
Diffstat (limited to 'aero-proto')
-rw-r--r--aero-proto/Cargo.toml1
-rw-r--r--aero-proto/src/dav/controller.rs183
2 files changed, 5 insertions, 179 deletions
diff --git a/aero-proto/Cargo.toml b/aero-proto/Cargo.toml
index b6f6336..e8d6b8f 100644
--- a/aero-proto/Cargo.toml
+++ b/aero-proto/Cargo.toml
@@ -7,6 +7,7 @@ license = "EUPL-1.2"
description = "Binding between Aerogramme's internal components and well-known protocols"
[dependencies]
+aero-ical.workspace = true
aero-sasl.workspace = true
aero-dav.workspace = true
aero-user.workspace = true
diff --git a/aero-proto/src/dav/controller.rs b/aero-proto/src/dav/controller.rs
index 4cf520e..873f768 100644
--- a/aero-proto/src/dav/controller.rs
+++ b/aero-proto/src/dav/controller.rs
@@ -1,6 +1,6 @@
use anyhow::Result;
use futures::stream::{StreamExt, TryStreamExt};
-use http_body_util::combinators::{BoxBody, UnsyncBoxBody};
+use http_body_util::combinators::UnsyncBoxBody;
use http_body_util::BodyStream;
use http_body_util::StreamBody;
use hyper::body::Frame;
@@ -11,10 +11,11 @@ use aero_collections::user::User;
use aero_dav::caltypes as cal;
use aero_dav::realization::All;
use aero_dav::types as dav;
+use aero_ical::query::is_component_match;
use crate::dav::codec;
use crate::dav::codec::{depth, deserialize, serialize, text_body};
-use crate::dav::node::{DavNode, PutPolicy};
+use crate::dav::node::DavNode;
use crate::dav::resource::RootNode;
pub(super) type ArcUser = std::sync::Arc<User>;
@@ -373,185 +374,9 @@ fn apply_filter<'a>(
tracing::debug!(filter=?root_filter, "calendar-query filter");
// Adjust return value according to filter
- match is_component_match(&[fake_vcal_component], root_filter) {
+ match is_component_match(&fake_vcal_component, &[fake_vcal_component.clone()], root_filter) {
true => Some(Ok(single_node)),
_ => None,
}
})
}
-
-fn ical_parse_date(dt: &str) -> Option<chrono::DateTime<chrono::Utc>> {
- tracing::trace!(raw_time = dt, "VEVENT raw time");
- let tmpl = match dt.chars().last() {
- Some('Z') => cal::UTC_DATETIME_FMT,
- Some(_) => {
- tracing::warn!(raw_time=dt, "floating datetime is not properly supported yet");
- cal::FLOATING_DATETIME_FMT
- },
- None => return None
- };
-
- NaiveDateTime::parse_from_str(dt, tmpl)
- .ok()
- .map(|v| v.and_utc())
-}
-
-fn prop_date(
- properties: &[icalendar::parser::Property],
- name: &str,
-) -> Option<chrono::DateTime<chrono::Utc>> {
- properties
- .iter()
- .find(|candidate| candidate.name.as_str() == name)
- .map(|p| p.val.as_str())
- .map(ical_parse_date)
- .flatten()
-}
-
-fn is_properties_match(props: &[icalendar::parser::Property], filters: &[cal::PropFilter]) -> bool {
- filters.iter().all(|single_filter| {
- // Find the property
- let single_prop = props
- .iter()
- .find(|candidate| candidate.name.as_str() == single_filter.name.0.as_str());
- match (&single_filter.additional_rules, single_prop) {
- (None, Some(_)) | (Some(cal::PropFilterRules::IsNotDefined), None) => true,
- (None, None)
- | (Some(cal::PropFilterRules::IsNotDefined), Some(_))
- | (Some(cal::PropFilterRules::Match(_)), None) => false,
- (Some(cal::PropFilterRules::Match(pattern)), Some(prop)) => {
- // check value
- match &pattern.time_or_text {
- Some(cal::TimeOrText::Time(time_range)) => {
- let maybe_parsed_date = ical_parse_date(prop.val.as_str());
-
- let parsed_date = match maybe_parsed_date {
- None => return false,
- Some(v) => v,
- };
-
- // see if entry is in range
- let is_in_range = match time_range {
- cal::TimeRange::OnlyStart(after) => &parsed_date >= after,
- cal::TimeRange::OnlyEnd(before) => &parsed_date <= before,
- cal::TimeRange::FullRange(after, before) => {
- &parsed_date >= after && &parsed_date <= before
- }
- };
- if !is_in_range {
- return false;
- }
-
- // if you are here, this subcondition is valid
- }
- Some(cal::TimeOrText::Text(txt_match)) => {
- //@FIXME ignoring collation
- let is_match = match txt_match.negate_condition {
- None | Some(false) => {
- prop.val.as_str().contains(txt_match.text.as_str())
- }
- Some(true) => !prop.val.as_str().contains(txt_match.text.as_str()),
- };
- if !is_match {
- return false;
- }
- }
- None => (), // if not filter on value is set, continue
- };
-
- // check parameters
- pattern.param_filter.iter().all(|single_param_filter| {
- let maybe_param = prop.params.iter().find(|candidate| {
- candidate.key.as_str() == single_param_filter.name.as_str()
- });
-
- match (maybe_param, &single_param_filter.additional_rules) {
- (Some(_), None) => true,
- (None, None) => false,
- (Some(_), Some(cal::ParamFilterMatch::IsNotDefined)) => false,
- (None, Some(cal::ParamFilterMatch::IsNotDefined)) => true,
- (None, Some(cal::ParamFilterMatch::Match(_))) => false,
- (Some(param), Some(cal::ParamFilterMatch::Match(txt_match))) => {
- let param_val = match &param.val {
- Some(v) => v,
- None => return false,
- };
-
- match txt_match.negate_condition {
- None | Some(false) => {
- param_val.as_str().contains(txt_match.text.as_str())
- }
- Some(true) => !param_val.as_str().contains(txt_match.text.as_str()),
- }
- }
- }
- })
- }
- }
- })
-}
-
-fn is_in_time_range(
- properties: &[icalendar::parser::Property],
- time_range: &cal::TimeRange,
-) -> bool {
- //@FIXME too naive: https://datatracker.ietf.org/doc/html/rfc4791#section-9.9
-
- let (dtstart, dtend) = match (
- prop_date(properties, "DTSTART"),
- prop_date(properties, "DTEND"),
- ) {
- (Some(start), None) => (start, start),
- (None, Some(end)) => (end, end),
- (Some(start), Some(end)) => (start, end),
- _ => {
- tracing::warn!("unable to extract DTSTART and DTEND from VEVENT");
- return false;
- }
- };
-
- tracing::trace!(event_start=?dtstart, event_end=?dtend, filter=?time_range, "apply filter on VEVENT");
- match time_range {
- cal::TimeRange::OnlyStart(after) => &dtend >= after,
- cal::TimeRange::OnlyEnd(before) => &dtstart <= before,
- cal::TimeRange::FullRange(after, before) => &dtend >= after && &dtstart <= before,
- }
-}
-
-use chrono::NaiveDateTime;
-fn is_component_match(
- components: &[icalendar::parser::Component],
- filter: &cal::CompFilter,
-) -> bool {
- // Find the component among the list
- let maybe_comp = components
- .iter()
- .find(|candidate| candidate.name.as_str() == filter.name.as_str());
-
- // Filter according to rules
- match (maybe_comp, &filter.additional_rules) {
- (Some(_), None) => true,
- (None, Some(cal::CompFilterRules::IsNotDefined)) => true,
- (None, None) => false,
- (Some(_), Some(cal::CompFilterRules::IsNotDefined)) => false,
- (None, Some(cal::CompFilterRules::Matches(_))) => false,
- (Some(component), Some(cal::CompFilterRules::Matches(matcher))) => {
- // check time range
- if let Some(time_range) = &matcher.time_range {
- if !is_in_time_range(component.properties.as_ref(), time_range) {
- return false;
- }
- }
-
- // check properties
- if !is_properties_match(component.properties.as_ref(), matcher.prop_filter.as_ref()) {
- return false;
- }
-
- // check inner components
- matcher.comp_filter.iter().all(|inner_filter| {
- is_component_match(component.components.as_ref(), &inner_filter)
- })
- }
- }
-}