From b906eea62daea43c70504ab66927d31dc7ca3543 Mon Sep 17 00:00:00 2001 From: mdecimus Date: Tue, 30 Sep 2025 18:05:20 +0200 Subject: [PATCH] Bump to calcard 0.2 --- Cargo.lock | 8 +- crates/common/Cargo.toml | 2 +- crates/dav-proto/Cargo.toml | 4 +- crates/dav-proto/src/parser/property.rs | 16 +-- crates/dav/Cargo.toml | 2 +- crates/dav/src/calendar/freebusy.rs | 23 ++-- crates/dav/src/calendar/query.rs | 14 +-- crates/dav/src/calendar/scheduling.rs | 4 +- crates/dav/src/card/query.rs | 4 +- crates/groupware/Cargo.toml | 2 +- crates/groupware/src/calendar/alarm.rs | 17 ++- crates/groupware/src/calendar/dates.rs | 4 +- crates/groupware/src/calendar/expand.rs | 2 +- crates/groupware/src/calendar/itip.rs | 13 ++- crates/groupware/src/contact/mod.rs | 1 - crates/groupware/src/scheduling/attendee.rs | 10 +- .../groupware/src/scheduling/event_cancel.rs | 2 +- crates/groupware/src/scheduling/inbound.rs | 40 +++---- crates/groupware/src/scheduling/itip.rs | 42 +++---- crates/groupware/src/scheduling/mod.rs | 4 +- crates/groupware/src/scheduling/organizer.rs | 10 +- crates/groupware/src/scheduling/snapshot.rs | 106 ++++++++++++------ crates/migration/Cargo.toml | 2 +- crates/services/Cargo.toml | 2 +- crates/services/src/task_manager/alarm.rs | 16 ++- crates/services/src/task_manager/imip.rs | 15 ++- tests/Cargo.toml | 2 +- tests/resources/itip/google_calendar.txt | 28 ++--- .../itip/rfc5546_event_recurring.txt | 20 ++-- tests/resources/itip/rfc5546_event_single.txt | 43 ++++--- tests/resources/itip/rfc5546_todo.txt | 6 +- tests/resources/itip/rfc6638_recurring.txt | 18 +-- tests/resources/itip/rfc6638_single.txt | 4 +- tests/src/webdav/cal_itip.rs | 6 +- tests/src/webdav/cal_query.rs | 6 +- tests/src/webdav/card_query.rs | 2 +- 36 files changed, 284 insertions(+), 216 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index d268672f..98524794 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -963,18 +963,19 @@ dependencies = [ [[package]] name = "calcard" -version = "0.1.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "720e412adf25f179f643b0753108cb308b812f82e1d34131c06b015c806e3f3c" +version = "0.2.0" dependencies = [ "ahash", "chrono", "chrono-tz", "hashify", + "jmap-tools", "mail-builder", "mail-parser", "rkyv", "serde", + "serde_json", + "uuid", ] [[package]] @@ -8903,6 +8904,7 @@ dependencies = [ "getrandom 0.3.3", "js-sys", "serde", + "sha1_smol", "wasm-bindgen", ] diff --git a/crates/common/Cargo.toml b/crates/common/Cargo.toml index 677fb759..49e80942 100644 --- a/crates/common/Cargo.toml +++ b/crates/common/Cargo.toml @@ -21,7 +21,7 @@ mail-auth = { version = "0.7.1" } mail-send = { version = "0.5", default-features = false, features = ["cram-md5", "ring", "tls12"] } smtp-proto = { version = "0.2", features = ["rkyv"] } dns-update = { version = "0.1.5" } -calcard = { version = "0.1.3", features = ["rkyv"] } +calcard = { path = "/Users/me/code/calcard", features = ["rkyv"] } ahash = { version = "0.8.2", features = ["serde"] } parking_lot = "0.12.1" regex = "1.7.0" diff --git a/crates/dav-proto/Cargo.toml b/crates/dav-proto/Cargo.toml index 73bb1472..f77a812d 100644 --- a/crates/dav-proto/Cargo.toml +++ b/crates/dav-proto/Cargo.toml @@ -7,7 +7,7 @@ edition = "2021" trc = { path = "../trc" } hashify = "0.2.6" quick-xml = { version = "0.38" } -calcard = { version = "0.1.3", features = ["rkyv"] } +calcard = { path = "/Users/me/code/calcard", features = ["rkyv"] } mail-parser = { version = "0.11", features = ["full_encoding", "rkyv"] } hyper = "1.6.0" rkyv = { version = "0.8.10", features = ["little_endian"] } @@ -15,7 +15,7 @@ chrono = { version = "0.4.40", features = ["serde"], optional = true } compact_str = "0.9.0" [dev-dependencies] -calcard = { version = "0.1.3", features = ["serde", "rkyv"] } +calcard = { path = "/Users/me/code/calcard", features = ["serde", "rkyv"] } serde = { version = "1.0.217", features = ["derive"] } serde_json = "1.0.138" chrono = { version = "0.4.40", features = ["serde"] } diff --git a/crates/dav-proto/src/parser/property.rs b/crates/dav-proto/src/parser/property.rs index 3d7a3bbc..b8f3a1bb 100644 --- a/crates/dav-proto/src/parser/property.rs +++ b/crates/dav-proto/src/parser/property.rs @@ -5,7 +5,7 @@ */ use calcard::{ - common::PartialDateTime, + common::{IanaParse, PartialDateTime}, icalendar::{ICalendar, ICalendarComponentType, ICalendarParameterName, ICalendarProperty}, vcard::{VCardParameterName, VCardProperty}, Entry, Parser, @@ -637,7 +637,7 @@ impl AttributeValue for ICalendarComponentType { where Self: Sized, { - ICalendarComponentType::try_from(s.as_bytes()).ok() + ICalendarComponentType::parse(s.as_bytes()) } } @@ -646,8 +646,8 @@ impl AttributeValue for ICalendarProperty { where Self: Sized, { - ICalendarProperty::try_from(s.as_bytes()) - .unwrap_or_else(|_| ICalendarProperty::Other(s.to_string())) + ICalendarProperty::parse(s.as_bytes()) + .unwrap_or_else(|| ICalendarProperty::Other(s.to_string())) .into() } } @@ -668,15 +668,15 @@ impl AttributeValue for VCardPropertyWithGroup { { if let Some((group, s)) = s.split_once('.') { VCardPropertyWithGroup { - name: VCardProperty::try_from(s.as_bytes()) - .unwrap_or_else(|_| VCardProperty::Other(s.to_string())), + name: VCardProperty::parse(s.as_bytes()) + .unwrap_or_else(|| VCardProperty::Other(s.to_string())), group: group.to_string().into(), } .into() } else { VCardPropertyWithGroup { - name: VCardProperty::try_from(s.as_bytes()) - .unwrap_or_else(|_| VCardProperty::Other(s.to_string())), + name: VCardProperty::parse(s.as_bytes()) + .unwrap_or_else(|| VCardProperty::Other(s.to_string())), group: None, } .into() diff --git a/crates/dav/Cargo.toml b/crates/dav/Cargo.toml index 60216b96..c444459a 100644 --- a/crates/dav/Cargo.toml +++ b/crates/dav/Cargo.toml @@ -14,7 +14,7 @@ directory = { path = "../directory" } http_proto = { path = "../http-proto" } types = { path = "../types" } trc = { path = "../trc" } -calcard = { version = "0.1.3", features = ["rkyv"] } +calcard = { path = "/Users/me/code/calcard", features = ["rkyv"] } hashify = { version = "0.2" } hyper = { version = "1.0.1", features = ["server", "http1", "http2"] } percent-encoding = "2.3.1" diff --git a/crates/dav/src/calendar/freebusy.rs b/crates/dav/src/calendar/freebusy.rs index 92dd3197..8f4e174c 100644 --- a/crates/dav/src/calendar/freebusy.rs +++ b/crates/dav/src/calendar/freebusy.rs @@ -9,11 +9,11 @@ use crate::{DavError, calendar::query::is_resource_in_time_range, common::uri::D use calcard::{ common::{PartialDateTime, timezone::Tz}, icalendar::{ - ArchivedICalendarComponentType, ArchivedICalendarEntry, ArchivedICalendarParameter, - ArchivedICalendarProperty, ArchivedICalendarStatus, ArchivedICalendarValue, ICalendar, - ICalendarComponent, ICalendarComponentType, ICalendarEntry, ICalendarFreeBusyType, - ICalendarParameter, ICalendarPeriod, ICalendarProperty, ICalendarTransparency, - ICalendarValue, + ArchivedICalendarComponentType, ArchivedICalendarEntry, ArchivedICalendarParameterName, + ArchivedICalendarParameterValue, ArchivedICalendarProperty, ArchivedICalendarStatus, + ArchivedICalendarValue, ICalendar, ICalendarComponent, ICalendarComponentType, + ICalendarEntry, ICalendarFreeBusyType, ICalendarParameter, ICalendarPeriod, + ICalendarProperty, ICalendarTransparency, ICalendarValue, }, }; use common::{DavResourcePath, DavResources, PROD_ID, Server, auth::AccessToken}; @@ -200,7 +200,7 @@ impl CalendarFreebusyRequestHandler for Server { } for (component_id, component) in components { - let component_id = component_id as u16; + let component_id = component_id as u32; match component.component_type { ArchivedICalendarComponentType::VEvent => { let fbtype = match component.status() { @@ -208,9 +208,6 @@ impl CalendarFreebusyRequestHandler for Server { Some(ArchivedICalendarStatus::Tentative) => { ICalendarFreeBusyType::BusyTentative } - Some(ArchivedICalendarStatus::Other(v)) => { - ICalendarFreeBusyType::Other(v.as_str().to_string()) - } _ => ICalendarFreeBusyType::Busy, }; @@ -240,8 +237,10 @@ impl CalendarFreebusyRequestHandler for Server { .params .iter() .find_map(|param| { - if let ArchivedICalendarParameter::Fbtype(param) = - param + if let ( + ArchivedICalendarParameterName::Fbtype, + ArchivedICalendarParameterValue::Fbtype(param), + ) = (¶m.name, ¶m.value) { rkyv_deserialize(param).ok() } else { @@ -263,7 +262,7 @@ impl CalendarFreebusyRequestHandler for Server { for (fbtype, events_in_range) in fb_entries { entries.push(ICalendarEntry { name: ICalendarProperty::Freebusy, - params: vec![ICalendarParameter::Fbtype(fbtype)], + params: vec![ICalendarParameter::fbtype(fbtype)], values: merge_intervals(events_in_range), }); } diff --git a/crates/dav/src/calendar/query.rs b/crates/dav/src/calendar/query.rs index 1ae28a50..7b587130 100644 --- a/crates/dav/src/calendar/query.rs +++ b/crates/dav/src/calendar/query.rs @@ -338,7 +338,7 @@ impl CalendarQueryHandler { FilterOp::Exists => true, FilterOp::Undefined => false, FilterOp::TextMatch(text_match) => { - if let Some(text) = entry.as_text() { + if let Some(text) = entry.value.as_text() { text_match.matches(text) } else { false @@ -363,7 +363,7 @@ impl CalendarQueryHandler { FilterOp::TimeRange(range) => { if !matches!(comp.last(), Some(ICalendarComponentType::VAlarm)) { let matching_comp_ids = find_components(ical, comp) - .map(|(id, comp)| (id as u16, &comp.component_type)) + .map(|(id, comp)| (id as u32, &comp.component_type)) .collect::>(); !matching_comp_ids.is_empty() @@ -381,14 +381,14 @@ impl CalendarQueryHandler { .data .alarms .iter() - .map(|alarm| alarm.parent_id.to_native()) + .map(|alarm| alarm.parent_id.to_native() as u32) .collect::>(); !matching_comp_ids.is_empty() && self.expanded_times.iter().any(|time| { matching_comp_ids.contains(&time.comp_id) && event.data.alarms.iter().any(|alarm| { - alarm.parent_id.to_native() == time.comp_id + alarm.parent_id.to_native() as u32 == time.comp_id && alarm .delta .to_timestamp( @@ -428,8 +428,8 @@ impl CalendarQueryHandler { ) -> Option { let mut out = String::with_capacity(event.size.to_native() as usize); let _v = [0.into()]; - let mut component_iter: Iter<'_, rkyv::rend::u16_le> = _v.iter(); - let mut component_stack: Vec<(&ArchivedICalendarComponent, Iter<'_, rkyv::rend::u16_le>)> = + let mut component_iter: Iter<'_, rkyv::rend::u32_le> = _v.iter(); + let mut component_stack: Vec<(&ArchivedICalendarComponent, Iter<'_, rkyv::rend::u32_le>)> = Vec::with_capacity(4); if data.expand.is_some() { @@ -653,5 +653,5 @@ fn find_parameter<'x>( entry: &'x ArchivedICalendarEntry, name: &ICalendarParameterName, ) -> Option<&'x ArchivedICalendarParameter> { - entry.params.iter().find(|param| param.matches_name(name)) + entry.params.iter().find(|param| param.name == *name) } diff --git a/crates/dav/src/calendar/scheduling.rs b/crates/dav/src/calendar/scheduling.rs index d2df9609..55638f52 100644 --- a/crates/dav/src/calendar/scheduling.rs +++ b/crates/dav/src/calendar/scheduling.rs @@ -282,10 +282,10 @@ impl CalendarSchedulingHandler for Server { let tz_id = entry.tz_id(); match (&entry.name, entry.values.first()) { (ICalendarProperty::Dtstart, Some(ICalendarValue::PartialDateTime(dt))) => { - from_date = dt.to_date_time_with_tz(tz_resolver.resolve(tz_id)); + from_date = dt.to_date_time_with_tz(tz_resolver.resolve_or_default(tz_id)); } (ICalendarProperty::Dtend, Some(ICalendarValue::PartialDateTime(dt))) => { - to_date = dt.to_date_time_with_tz(tz_resolver.resolve(tz_id)); + to_date = dt.to_date_time_with_tz(tz_resolver.resolve_or_default(tz_id)); } (ICalendarProperty::Uid, Some(ICalendarValue::Text(_))) => { uid = Some(entry); diff --git a/crates/dav/src/card/query.rs b/crates/dav/src/card/query.rs index 06d8db99..6ddac964 100644 --- a/crates/dav/src/card/query.rs +++ b/crates/dav/src/card/query.rs @@ -157,7 +157,7 @@ pub(crate) fn vcard_query(card: &ArchivedVCard, filters: &AddressbookFilter) -> FilterOp::Exists => true, FilterOp::Undefined => false, FilterOp::TextMatch(text_match) => { - if let Some(text) = entry.as_text() { + if let Some(text) = entry.value.as_text() { text_match.matches(text) } else { false @@ -197,7 +197,7 @@ fn find_parameter<'x>( entry: &'x ArchivedVCardEntry, name: &VCardParameterName, ) -> Option<&'x ArchivedVCardParameter> { - entry.params.iter().find(|param| param.matches_name(name)) + entry.params.iter().find(|param| param.name == *name) } pub(crate) fn serialize_vcard_with_props( diff --git a/crates/groupware/Cargo.toml b/crates/groupware/Cargo.toml index dad570ca..0c7e1f90 100644 --- a/crates/groupware/Cargo.toml +++ b/crates/groupware/Cargo.toml @@ -12,7 +12,7 @@ types = { path = "../types" } trc = { path = "../trc" } directory = { path = "../directory" } dav-proto = { path = "../dav-proto" } -calcard = { version = "0.1.3", features = ["rkyv"] } +calcard = { path = "/Users/me/code/calcard", features = ["rkyv"] } hashify = "0.2" tokio = { version = "1.47", features = ["net", "macros"] } rkyv = { version = "0.8.10", features = ["little_endian"] } diff --git a/crates/groupware/src/calendar/alarm.rs b/crates/groupware/src/calendar/alarm.rs index ce6f89a2..162ef72d 100644 --- a/crates/groupware/src/calendar/alarm.rs +++ b/crates/groupware/src/calendar/alarm.rs @@ -8,7 +8,8 @@ use super::{Alarm, AlarmDelta, ArchivedAlarmDelta, ArchivedCalendarEventData}; use calcard::{ common::timezone::Tz, icalendar::{ - ICalendarComponent, ICalendarParameter, ICalendarProperty, ICalendarValue, Related, + ICalendarComponent, ICalendarParameterName, ICalendarParameterValue, ICalendarProperty, + ICalendarRelated, ICalendarValue, }, }; use chrono::{DateTime, TimeZone}; @@ -176,11 +177,17 @@ impl ExpandAlarm for ICalendarComponent { let mut trigger_start = true; for param in entry.params.iter() { - match param { - ICalendarParameter::Related(related) => { - trigger_start = matches!(related, Related::Start); + match (¶m.name, ¶m.value) { + ( + ICalendarParameterName::Related, + ICalendarParameterValue::Related(related), + ) => { + trigger_start = matches!(related, ICalendarRelated::Start); } - ICalendarParameter::Tzid(tz_id) => { + ( + ICalendarParameterName::Tzid, + ICalendarParameterValue::Text(tz_id), + ) => { tz = Tz::from_str(tz_id).ok(); } _ => {} diff --git a/crates/groupware/src/calendar/dates.rs b/crates/groupware/src/calendar/dates.rs index 938c7af7..50154f13 100644 --- a/crates/groupware/src/calendar/dates.rs +++ b/crates/groupware/src/calendar/dates.rs @@ -69,7 +69,7 @@ impl CalendarEventData { .filter_map(|alarm_id| { ical.component_by_id(*alarm_id).and_then(|alarm| { if alarm.component_type == ICalendarComponentType::VAlarm { - alarm.expand_alarm(*alarm_id, event.comp_id) + alarm.expand_alarm(*alarm_id as u16, event.comp_id as u16) } else { None } @@ -122,7 +122,7 @@ impl CalendarEventData { .entry(( start_tz, end_tz, - event.comp_id, + event.comp_id as u16, (end_timestamp_naive - start_timestamp_naive) as i32, )) .or_default() diff --git a/crates/groupware/src/calendar/expand.rs b/crates/groupware/src/calendar/expand.rs index 58d00345..5c7d2c5c 100644 --- a/crates/groupware/src/calendar/expand.rs +++ b/crates/groupware/src/calendar/expand.rs @@ -21,7 +21,7 @@ impl ArchivedCalendarEventData { let instances = range.instances.as_ref(); let (offset_or_count, bytes_read) = instances.read_leb128::()?; - let comp_id = range.id.to_native(); + let comp_id = range.id.to_native() as u32; let duration = range.duration.to_native() as i64; let mut start_tz = Tz::from_id(range.start_tz.to_native())?; let mut end_tz = Tz::from_id(range.end_tz.to_native())?; diff --git a/crates/groupware/src/calendar/itip.rs b/crates/groupware/src/calendar/itip.rs index 38609ca3..3e3105e0 100644 --- a/crates/groupware/src/calendar/itip.rs +++ b/crates/groupware/src/calendar/itip.rs @@ -17,10 +17,11 @@ use crate::{ }, }; use calcard::{ - common::timezone::Tz, + common::{IanaString, timezone::Tz}, icalendar::{ ICalendar, ICalendarComponentType, ICalendarEntry, ICalendarMethod, ICalendarParameter, - ICalendarParticipationStatus, ICalendarProperty, ICalendarValue, + ICalendarParameterName, ICalendarParameterValue, ICalendarParticipationStatus, + ICalendarProperty, ICalendarValue, }, }; use common::{ @@ -422,7 +423,11 @@ impl ItipIngest for Server { { let mut add_partstat = true; for param in &mut entry.params { - if let ICalendarParameter::Partstat(partstat) = param { + if let ( + ICalendarParameterName::Partstat, + ICalendarParameterValue::Partstat(partstat), + ) = (¶m.name, &mut param.value) + { if partstat != &rsvp.partstat { *partstat = rsvp.partstat.clone(); add_partstat = false; @@ -435,7 +440,7 @@ impl ItipIngest for Server { if add_partstat { entry .params - .push(ICalendarParameter::Partstat(rsvp.partstat.clone())); + .push(ICalendarParameter::partstat(rsvp.partstat.clone())); } found_participant = true; did_change = true; diff --git a/crates/groupware/src/contact/mod.rs b/crates/groupware/src/contact/mod.rs index cf819ca5..e5c659b0 100644 --- a/crates/groupware/src/contact/mod.rs +++ b/crates/groupware/src/contact/mod.rs @@ -39,7 +39,6 @@ pub enum AddressBookRight { #[derive( rkyv::Archive, rkyv::Deserialize, rkyv::Serialize, Debug, Default, Clone, PartialEq, Eq, )] -#[rkyv(derive(Debug))] pub struct ContactCard { pub names: Vec, pub display_name: Option, diff --git a/crates/groupware/src/scheduling/attendee.rs b/crates/groupware/src/scheduling/attendee.rs index af5564ff..7b10fdcf 100644 --- a/crates/groupware/src/scheduling/attendee.rs +++ b/crates/groupware/src/scheduling/attendee.rs @@ -68,7 +68,7 @@ pub(crate) fn attendee_handle_update( part_stat = &ICalendarParticipationStatus::Declined; // Add cancel component - let comp_id = message.components.len() as u16; + let comp_id = message.components.len() as u32; message.components[0].component_ids.push(comp_id); message.components.push(cancel_comp); mail_from = Some(&attendee_email.email); @@ -132,7 +132,7 @@ pub(crate) fn attendee_handle_update( attendee_entry_uids.push(external_attendee.entry_id); } - let comp_id = message.components.len() as u16; + let comp_id = message.components.len() as u32; message.components[0].component_ids.push(comp_id); message.components.push(itip_export_component( instance.comp, @@ -183,7 +183,7 @@ pub(crate) fn attendee_handle_update( } // A new instance has been added - let comp_id = message.components.len() as u16; + let comp_id = message.components.len() as u32; message.components[0].component_ids.push(comp_id); message.components.push(itip_export_component( instance.comp, @@ -211,7 +211,7 @@ pub(crate) fn attendee_handle_update( false, ) { // Add cancel component - let comp_id = message.components.len() as u16; + let comp_id = message.components.len() as u32; message.components[0].component_ids.push(comp_id); message.components.push(cancel_comp); mail_from = Some(&attendee_email.email); @@ -313,7 +313,7 @@ pub(crate) fn attendee_decline<'x>( ); cancel_comp.add_property_with_params( ICalendarProperty::Attendee, - [ICalendarParameter::Partstat( + [ICalendarParameter::partstat( ICalendarParticipationStatus::Declined, )], ICalendarValue::Text(local_attendee.email.to_string()), diff --git a/crates/groupware/src/scheduling/event_cancel.rs b/crates/groupware/src/scheduling/event_cancel.rs index a047e8f3..27eac0a7 100644 --- a/crates/groupware/src/scheduling/event_cancel.rs +++ b/crates/groupware/src/scheduling/event_cancel.rs @@ -98,7 +98,7 @@ pub fn itip_cancel( is_deletion, ) { // Add cancel component - let comp_id = message.components.len() as u16; + let comp_id = message.components.len() as u32; message.components[0].component_ids.push(comp_id); message.components.push(cancel_comp); mail_from = Some(&attendee_email.email); diff --git a/crates/groupware/src/scheduling/inbound.rs b/crates/groupware/src/scheduling/inbound.rs index a5c749ed..f629779b 100644 --- a/crates/groupware/src/scheduling/inbound.rs +++ b/crates/groupware/src/scheduling/inbound.rs @@ -371,33 +371,33 @@ fn handle_reply( if changed_part_stat { remove_parameters.push(ICalendarParameterName::Partstat); if let Some(part_stat) = updated_attendee.part_stat { - add_parameters.push(ICalendarParameter::Partstat(part_stat.clone())); + add_parameters.push(ICalendarParameter::partstat(part_stat.clone())); } } if changed_rsvp { remove_parameters.push(ICalendarParameterName::Rsvp); if let Some(rsvp) = updated_attendee.rsvp { - add_parameters.push(ICalendarParameter::Rsvp(rsvp)); + add_parameters.push(ICalendarParameter::rsvp(rsvp)); } } if changed_delegated_to { remove_parameters.push(ICalendarParameterName::DelegatedTo); if !updated_attendee.delegated_to.is_empty() { - add_parameters.push(ICalendarParameter::DelegatedTo( - updated_attendee - .delegated_to - .iter() - .map(|email| Uri::Location(email.to_string())) - .collect::>(), + add_parameters.extend(updated_attendee.delegated_to.iter().map( + |email| { + ICalendarParameter::delegated_to(Uri::Location( + email.to_string(), + )) + }, )); } } if has_request_status { remove_parameters.push(ICalendarParameterName::ScheduleStatus); - add_parameters.push(ICalendarParameter::ScheduleStatus( + add_parameters.push(ICalendarParameter::schedule_status( itip_snapshot.request_status.join(","), )); } @@ -442,13 +442,13 @@ fn handle_reply( .iter() .filter(|param| { matches!( - param, - ICalendarParameter::DelegatedTo(_) - | ICalendarParameter::DelegatedFrom(_) - | ICalendarParameter::Partstat(_) - | ICalendarParameter::Rsvp(_) - | ICalendarParameter::ScheduleStatus(_) - | ICalendarParameter::Role(_) + param.name, + ICalendarParameterName::DelegatedTo + | ICalendarParameterName::DelegatedFrom + | ICalendarParameterName::Partstat + | ICalendarParameterName::Rsvp + | ICalendarParameterName::ScheduleStatus + | ICalendarParameterName::Role ) }) .cloned() @@ -540,7 +540,7 @@ fn handle_reply( } pub fn itip_merge_changes(ical: &mut ICalendar, changes: Vec) { - let mut remove_component_ids = Vec::new(); + let mut remove_component_ids: Vec = Vec::new(); for action in changes { match action { MergeAction::AddEntries { @@ -575,10 +575,10 @@ pub fn itip_merge_changes(ical: &mut ICalendar, changes: Vec) { } => { ical.components[component_id as usize].entries[entry_id as usize] .params - .retain(|param| !parameters.iter().any(|p| param.matches_name(p))); + .retain(|param| !parameters.contains(¶m.name)); } MergeAction::AddComponent { component } => { - let comp_id = ical.components.len() as u16; + let comp_id = ical.components.len() as u32; if let Some(root) = ical .components .get_mut(0) @@ -589,7 +589,7 @@ pub fn itip_merge_changes(ical: &mut ICalendar, changes: Vec) { } } MergeAction::RemoveComponent { component_id } => { - remove_component_ids.push(component_id); + remove_component_ids.push(component_id as u32); } } } diff --git a/crates/groupware/src/scheduling/itip.rs b/crates/groupware/src/scheduling/itip.rs index 46bb33ca..48909596 100644 --- a/crates/groupware/src/scheduling/itip.rs +++ b/crates/groupware/src/scheduling/itip.rs @@ -6,10 +6,11 @@ use crate::scheduling::{ArchivedItipSummary, ItipMessage, ItipMessages}; use calcard::{ - common::PartialDateTime, + common::{IanaString, PartialDateTime}, icalendar::{ ICalendar, ICalendarComponent, ICalendarComponentType, ICalendarEntry, ICalendarMethod, - ICalendarParameter, ICalendarParticipationStatus, ICalendarProperty, ICalendarValue, + ICalendarParameter, ICalendarParameterName, ICalendarParameterValue, + ICalendarParticipationStatus, ICalendarProperty, ICalendarValue, }, }; use common::PROD_ID; @@ -81,16 +82,19 @@ pub(crate) fn itip_export_component( let mut rsvp = true; for entry in &entry.params { - match entry { - ICalendarParameter::ScheduleStatus(_) - | ICalendarParameter::ScheduleAgent(_) - | ICalendarParameter::ScheduleForceSend(_) => {} + match &entry.name { + ICalendarParameterName::ScheduleStatus + | ICalendarParameterName::ScheduleAgent + | ICalendarParameterName::ScheduleForceSend => {} _ => { - match entry { - ICalendarParameter::Rsvp(false) => { - rsvp = false; + match &entry.name { + ICalendarParameterName::Rsvp => { + rsvp = !matches!( + entry.value, + ICalendarParameterValue::Bool(false) + ); } - ICalendarParameter::Partstat(_) => { + ICalendarParameterName::Partstat => { has_partstat = true; } _ => {} @@ -104,7 +108,7 @@ pub(crate) fn itip_export_component( if !has_partstat && rsvp && entry.name == ICalendarProperty::Attendee { new_entry .params - .push(ICalendarParameter::Partstat((*partstat).clone())); + .push(ICalendarParameter::partstat((*partstat).clone())); } comp.entries.push(new_entry); @@ -123,10 +127,10 @@ pub(crate) fn itip_export_component( .iter() .filter(|param| { !matches!( - param, - ICalendarParameter::ScheduleStatus(_) - | ICalendarParameter::ScheduleAgent(_) - | ICalendarParameter::ScheduleForceSend(_) + ¶m.name, + ICalendarParameterName::ScheduleStatus + | ICalendarParameterName::ScheduleAgent + | ICalendarParameterName::ScheduleForceSend ) }) .cloned() @@ -190,9 +194,9 @@ pub(crate) fn itip_finalize(ical: &mut ICalendar, scheduling_object_ids: &[u16]) entry.name, ICalendarProperty::Organizer | ICalendarProperty::Attendee ) { - entry - .params - .retain(|param| !matches!(param, ICalendarParameter::ScheduleForceSend(_))); + entry.params.retain(|param| { + !matches!(param.name, ICalendarParameterName::ScheduleForceSend) + }); } } } @@ -229,7 +233,7 @@ pub(crate) fn itip_add_tz(message: &mut ICalendar, ical: &ICalendar) { && c.entries.iter().any(|e| { e.params .iter() - .any(|p| matches!(p, ICalendarParameter::Tzid(_))) + .any(|p| matches!(p.name, ICalendarParameterName::Tzid)) }) }) && !has_timezones { diff --git a/crates/groupware/src/scheduling/mod.rs b/crates/groupware/src/scheduling/mod.rs index 60cd2302..3c52fb24 100644 --- a/crates/groupware/src/scheduling/mod.rs +++ b/crates/groupware/src/scheduling/mod.rs @@ -6,7 +6,7 @@ use ahash::{AHashMap, AHashSet}; use calcard::{ - common::PartialDateTime, + common::{IanaString, PartialDateTime}, icalendar::{ ICalendarComponent, ICalendarDuration, ICalendarEntry, ICalendarMethod, ICalendarParameter, ICalendarParticipationRole, ICalendarParticipationStatus, ICalendarPeriod, @@ -333,7 +333,7 @@ impl ItipDateTime<'_> { name, params: self .tz_id - .map(|tz_id| vec![ICalendarParameter::Tzid(tz_id.to_string())]) + .map(|tz_id| vec![ICalendarParameter::tzid(tz_id.to_string())]) .unwrap_or_default(), values: vec![ICalendarValue::PartialDateTime(Box::new(self.date.clone()))], } diff --git a/crates/groupware/src/scheduling/organizer.rs b/crates/groupware/src/scheduling/organizer.rs index 1d2a5800..12f7cf07 100644 --- a/crates/groupware/src/scheduling/organizer.rs +++ b/crates/groupware/src/scheduling/organizer.rs @@ -268,7 +268,7 @@ pub(crate) fn organizer_handle_update( }; // Add component to message - let comp_id = message.components.len() as u16; + let comp_id = message.components.len() as u32; message.components.push(component); message.components[0].component_ids.push(comp_id); } @@ -359,7 +359,9 @@ pub(crate) fn organizer_request_full( // Add component to message message.components[comp.comp_id as usize] = component; - message.components[0].component_ids.push(comp.comp_id); + message.components[0] + .component_ids + .push(comp.comp_id as u32); // Add attendees for attendee in &comp.attendees { @@ -375,8 +377,8 @@ pub(crate) fn organizer_request_full( for (comp_id, comp) in ical.components.iter().enumerate() { if matches!(comp.component_type, ICalendarComponentType::VTimezone) { copy_components.extend(comp.component_ids.iter().copied()); - message.components[0].component_ids.push(comp_id as u16); - } else if !copy_components.contains(&(comp_id as u16)) { + message.components[0].component_ids.push(comp_id as u32); + } else if !copy_components.contains(&(comp_id as u32)) { continue; } message.components[comp_id] = comp.clone(); diff --git a/crates/groupware/src/scheduling/snapshot.rs b/crates/groupware/src/scheduling/snapshot.rs index cd15bfd6..e887eb81 100644 --- a/crates/groupware/src/scheduling/snapshot.rs +++ b/crates/groupware/src/scheduling/snapshot.rs @@ -10,8 +10,8 @@ use crate::scheduling::{ }; use ahash::AHashMap; use calcard::icalendar::{ - ICalendar, ICalendarParameter, ICalendarProperty, ICalendarScheduleAgentValue, ICalendarValue, - Uri, + ICalendar, ICalendarParameterName, ICalendarParameterValue, ICalendarProperty, + ICalendarScheduleAgentValue, ICalendarValue, Uri, }; pub fn itip_snapshot<'x, 'y>( @@ -78,15 +78,26 @@ pub fn itip_snapshot<'x, 'y>( has_local_emails |= part.email.is_local; for param in &entry.params { - match param { - ICalendarParameter::ScheduleAgent(agent) => { - part.is_server_scheduling = - agent == &ICalendarScheduleAgentValue::Server; + match (¶m.name, ¶m.value) { + ( + ICalendarParameterName::ScheduleAgent, + ICalendarParameterValue::ScheduleAgent( + ICalendarScheduleAgentValue::Client + | ICalendarScheduleAgentValue::None, + ), + ) => { + part.is_server_scheduling = false; } - ICalendarParameter::ScheduleForceSend(force_send) => { + ( + ICalendarParameterName::ScheduleForceSend, + ICalendarParameterValue::ScheduleForceSend(force_send), + ) => { part.force_send = Some(force_send); } - ICalendarParameter::Cn(name) => { + ( + ICalendarParameterName::Cn, + ICalendarParameterValue::Text(name), + ) => { part.name = Some(name.as_str()); } _ => {} @@ -133,42 +144,70 @@ pub fn itip_snapshot<'x, 'y>( }; for param in &entry.params { - match param { - ICalendarParameter::ScheduleAgent(agent) => { + match (¶m.name, ¶m.value) { + ( + ICalendarParameterName::ScheduleAgent, + ICalendarParameterValue::ScheduleAgent(agent), + ) => { part.is_server_scheduling = agent == &ICalendarScheduleAgentValue::Server; } - ICalendarParameter::Rsvp(rsvp) => { + ( + ICalendarParameterName::Rsvp, + ICalendarParameterValue::Bool(rsvp), + ) => { part.rsvp = Some(*rsvp); } - ICalendarParameter::ScheduleForceSend(force_send) => { + ( + ICalendarParameterName::ScheduleForceSend, + ICalendarParameterValue::ScheduleForceSend(force_send), + ) => { part.force_send = Some(force_send); } - ICalendarParameter::Partstat(value) => { + ( + ICalendarParameterName::Partstat, + ICalendarParameterValue::Partstat(value), + ) => { part.part_stat = Some(value); } - ICalendarParameter::Cutype(value) => { + ( + ICalendarParameterName::Cutype, + ICalendarParameterValue::Cutype(value), + ) => { part.cu_type = Some(value); } - ICalendarParameter::DelegatedFrom(value) => { - part.delegated_from = value - .iter() - .filter_map(|uri| Email::from_uri(uri, account_emails)) - .collect(); + ( + ICalendarParameterName::DelegatedFrom, + ICalendarParameterValue::Uri(uri), + ) => { + if let Some(uri) = Email::from_uri(uri, account_emails) { + part.delegated_from.push(uri); + } } - ICalendarParameter::DelegatedTo(value) => { - part.delegated_to = value - .iter() - .filter_map(|uri| Email::from_uri(uri, account_emails)) - .collect(); + ( + ICalendarParameterName::DelegatedTo, + ICalendarParameterValue::Uri(uri), + ) => { + if let Some(uri) = Email::from_uri(uri, account_emails) { + part.delegated_to.push(uri); + } } - ICalendarParameter::Role(value) => { + ( + ICalendarParameterName::Role, + ICalendarParameterValue::Role(value), + ) => { part.role = Some(value); } - ICalendarParameter::SentBy(value) => { + ( + ICalendarParameterName::SentBy, + ICalendarParameterValue::Uri(value), + ) => { part.sent_by = Email::from_uri(value, account_emails); } - ICalendarParameter::Cn(name) => { + ( + ICalendarParameterName::Cn, + ICalendarParameterValue::Text(name), + ) => { part.name = Some(name.as_str()); } _ => {} @@ -213,11 +252,14 @@ pub fn itip_snapshot<'x, 'y>( let mut tz_id = None; for param in &entry.params { - match param { - ICalendarParameter::Tzid(id) => { + match (¶m.name, ¶m.value) { + ( + ICalendarParameterName::Tzid, + ICalendarParameterValue::Text(id), + ) => { tz_id = Some(id.as_str()); } - ICalendarParameter::Range => { + (ICalendarParameterName::Range, _) => { this_and_future = true; } _ => (), @@ -230,7 +272,7 @@ pub fn itip_snapshot<'x, 'y>( .to_date_time_with_tz( tz_resolver .get_or_insert_with(|| ical.build_tz_resolver()) - .resolve(tz_id), + .resolve_or_default(tz_id), ) .map(|dt| dt.timestamp()) .unwrap_or_else(|| date.to_timestamp().unwrap_or_default()), @@ -270,7 +312,7 @@ pub fn itip_snapshot<'x, 'y>( ICalendarValue::PartialDateTime(date) => { let tz = tz_resolver .get_or_insert_with(|| ical.build_tz_resolver()) - .resolve(tz_id); + .resolve_or_default(tz_id); ItipEntryValue::DateTime(ItipDateTime { date: date.as_ref(), tz_id, diff --git a/crates/migration/Cargo.toml b/crates/migration/Cargo.toml index 67289aba..1ca08b98 100644 --- a/crates/migration/Cargo.toml +++ b/crates/migration/Cargo.toml @@ -19,7 +19,7 @@ dav-proto = { path = "../dav-proto" } mail-parser = { version = "0.11", features = ["full_encoding"] } mail-auth = { version = "0.7.1", features = ["rkyv"] } sieve-rs = { version = "0.7", features = ["rkyv"] } -calcard = { version = "0.1.3", features = ["rkyv"] } +calcard = { path = "/Users/me/code/calcard", features = ["rkyv"] } tokio = { version = "1.47", features = ["net", "macros"] } serde = { version = "1.0", features = ["derive"]} serde_json = "1.0" diff --git a/crates/services/Cargo.toml b/crates/services/Cargo.toml index c71524dc..8f06b57e 100644 --- a/crates/services/Cargo.toml +++ b/crates/services/Cargo.toml @@ -19,7 +19,7 @@ smtp-proto = { version = "0.2", features = ["rkyv", "serde"] } tokio = { version = "1.47", features = ["rt"] } mail-parser = { version = "0.11", features = ["full_encoding", "rkyv"] } mail-builder = { version = "0.4" } -calcard = { version = "0.1.3", features = ["rkyv"] } +calcard = { path = "/Users/me/code/calcard", features = ["rkyv"] } chrono = { version = "0.4", features = ["unstable-locales"] } serde = { version = "1.0", features = ["derive"]} serde_json = "1.0" diff --git a/crates/services/src/task_manager/alarm.rs b/crates/services/src/task_manager/alarm.rs index e14630ac..a2416118 100644 --- a/crates/services/src/task_manager/alarm.rs +++ b/crates/services/src/task_manager/alarm.rs @@ -7,7 +7,7 @@ use super::Task; use calcard::{ common::timezone::Tz, - icalendar::{ArchivedICalendarParameter, ArchivedICalendarProperty}, + icalendar::{ArchivedICalendarParameterName, ArchivedICalendarProperty}, }; use chrono::{DateTime, Locale}; use common::{ @@ -391,8 +391,8 @@ async fn build_template( .and_then(|v| v.as_text()) .map(|v| v.strip_prefix("mailto:").unwrap_or(v)); let name = entry.params.iter().find_map(|param| { - if let ArchivedICalendarParameter::Cn(name) = param { - Some(name.as_str()) + if let ArchivedICalendarParameterName::Cn = param.name { + param.value.as_text() } else { None } @@ -456,14 +456,20 @@ async fn build_template( DateTime::from_timestamp(alarm.event_start, 0) .unwrap_or_default() .format_localized(locale.calendar_date_template, chrono_locale), - Tz::from_id(alarm.event_start_tz).unwrap_or(Tz::UTC).name() + Tz::from_id(alarm.event_start_tz) + .unwrap_or(Tz::UTC) + .name() + .unwrap_or_default() ); let end = format!( "{} ({})", DateTime::from_timestamp(alarm.event_end, 0) .unwrap_or_default() .format_localized(locale.calendar_date_template, chrono_locale), - Tz::from_id(alarm.event_end_tz).unwrap_or(Tz::UTC).name() + Tz::from_id(alarm.event_end_tz) + .unwrap_or(Tz::UTC) + .name() + .unwrap_or_default() ); let subject = format!( "{}: {} @ {}", diff --git a/crates/services/src/task_manager/imip.rs b/crates/services/src/task_manager/imip.rs index f543cc69..39c7dda1 100644 --- a/crates/services/src/task_manager/imip.rs +++ b/crates/services/src/task_manager/imip.rs @@ -8,9 +8,9 @@ use crate::task_manager::Task; use calcard::{ common::timezone::Tz, icalendar::{ - ArchivedICalendarDay, ArchivedICalendarFrequency, ArchivedICalendarParticipationStatus, - ArchivedICalendarRecurrenceRule, ArchivedICalendarWeekday, ICalendarParticipationStatus, - ICalendarProperty, + ArchivedICalendarDay, ArchivedICalendarFrequency, ArchivedICalendarMonth, + ArchivedICalendarParticipationStatus, ArchivedICalendarRecurrenceRule, + ArchivedICalendarWeekday, ICalendarParticipationStatus, ICalendarProperty, }, }; use chrono::{DateTime, Locale}; @@ -587,7 +587,7 @@ fn format_field(value: &ArchivedItipValue, template: &str, chrono_locale: Locale .naive_local() ) .format_localized(template, chrono_locale), - tz.name() + tz.name().unwrap_or_default() ) } ArchivedItipValue::Rrule(rrule) => RecurrenceFormatter.format(rrule), @@ -758,8 +758,11 @@ impl RecurrenceFormatter { format!("on the {}", self.format_list(&day_strings)) } - fn format_months(&self, months: &[u8]) -> String { - let month_names: Vec = months.iter().map(|&month| self.month_name(month)).collect(); + fn format_months(&self, months: &[ArchivedICalendarMonth]) -> String { + let month_names: Vec = months + .iter() + .map(|month| self.month_name(month.month())) + .collect(); format!("in {}", self.format_list(&month_names)) } diff --git a/tests/Cargo.toml b/tests/Cargo.toml index 92b9e86f..bcf6786b 100644 --- a/tests/Cargo.toml +++ b/tests/Cargo.toml @@ -30,7 +30,7 @@ imap_proto = { path = "../crates/imap-proto" } types = { path = "../crates/types" } dav = { path = "../crates/dav", features = ["test_mode"] } dav-proto = { path = "../crates/dav-proto", features = ["test_mode"] } -calcard = { version = "0.1.3", features = ["rkyv"] } +calcard = { path = "/Users/me/code/calcard", features = ["rkyv"] } groupware = { path = "../crates/groupware", features = ["test_mode"] } http = { path = "../crates/http", features = ["test_mode", "enterprise"] } http_proto = { path = "../crates/http-proto" } diff --git a/tests/resources/itip/google_calendar.txt b/tests/resources/itip/google_calendar.txt index dd147ef7..7ebeacb2 100644 --- a/tests/resources/itip/google_calendar.txt +++ b/tests/resources/itip/google_calendar.txt @@ -57,13 +57,14 @@ summary: invite summary.attendee: Participants([ItipParticipant { email: "a@gmail.com", name: Some("John Doe"), is_organizer: true }, ItipParticipant { email: "b@gmail.com", name: Some("b@gmail.com"), is_organizer: false }]) summary.description: Text("This is the event description") summary.dtstart: Time(ItipTime { start: 1750338000, tz_id: 148 }) -summary.rrule: Rrule(ICalendarRecurrenceRule { freq: Weekly, until: None, count: Some(2), interval: None, bysecond: [], byminute: [], byhour: [], byday: [ICalendarDay { ordwk: None, weekday: Thursday }], bymonthday: [], byyearday: [], byweekno: [], bymonth: [], bysetpos: [], wkst: Some(Sunday) }) +summary.rrule: Rrule(ICalendarRecurrenceRule { freq: Weekly, until: None, count: Some(2), interval: None, bysecond: [], byminute: [], byhour: [], byday: [ICalendarDay { ordwk: None, weekday: Thursday }], bymonthday: [], byyearday: [], byweekno: [], bymonth: [], bysetpos: [], wkst: Some(Sunday), rscale: None, skip: None }) summary.summary: Text("Meet me maybe") BEGIN:VCALENDAR METHOD:REQUEST PRODID:-//Stalwart Labs LLC//Stalwart Server//EN VERSION:2.0 BEGIN:VEVENT +X-MICROSOFT-CDO-OWNERAPPTID:299828133 DESCRIPTION:This is the event description LOCATION: STATUS:CONFIRMED @@ -71,8 +72,8 @@ SUMMARY:Meet me maybe DTEND;TZID=America/Los_Angeles:20250619T064500 DTSTART;TZID=America/Los_Angeles:20250619T060000 TRANSP:OPAQUE -ATTENDEE;CUTYPE=INDIVIDUAL;ROLE=REQ-PARTICIPANT;PARTSTAT=NEEDS-ACTION;RSVP= - TRUE;CN=b@gmail.com;X-NUM-GUESTS=0:mailto:b@gmail.com +ATTENDEE;CUTYPE=INDIVIDUAL;ROLE=REQ-PARTICIPANT;PARTSTAT=NEEDS-ACTION;RSVP=TRUE; + CN=b@gmail.com;X-NUM-GUESTS=0:mailto:b@gmail.com ATTENDEE;CUTYPE=INDIVIDUAL;ROLE=REQ-PARTICIPANT;PARTSTAT=ACCEPTED;RSVP=TRUE; CN="John Doe";X-NUM-GUESTS=0:mailto:a@gmail.com ORGANIZER;CN="John Doe":mailto:a@gmail.com @@ -82,7 +83,6 @@ CREATED:20250616T182358Z DTSTAMP:0 LAST-MODIFIED:20250616T182358Z SEQUENCE:1 -X-MICROSOFT-CDO-OWNERAPPTID:299828133 BEGIN:VALARM DESCRIPTION:This is an event reminder ACTION:DISPLAY @@ -90,8 +90,8 @@ TRIGGER:-PT10M END:VALARM END:VEVENT BEGIN:VTIMEZONE -TZID:America/Los_Angeles X-LIC-LOCATION:America/Los_Angeles +TZID:America/Los_Angeles BEGIN:DAYLIGHT DTSTART:19700308T020000 TZNAME:PDT @@ -171,7 +171,7 @@ summary: update REQUEST summary.attendee: Participants([ItipParticipant { email: "a@gmail.com", name: Some("John Doe"), is_organizer: true }, ItipParticipant { email: "b@gmail.com", name: Some("b@gmail.com"), is_organizer: false }]) summary.description: Text("This is the updated event description") summary.dtstart: Time(ItipTime { start: 1750338000, tz_id: 148 }) -summary.rrule: Rrule(ICalendarRecurrenceRule { freq: Weekly, until: None, count: Some(2), interval: None, bysecond: [], byminute: [], byhour: [], byday: [ICalendarDay { ordwk: None, weekday: Thursday }], bymonthday: [], byyearday: [], byweekno: [], bymonth: [], bysetpos: [], wkst: Some(Sunday) }) +summary.rrule: Rrule(ICalendarRecurrenceRule { freq: Weekly, until: None, count: Some(2), interval: None, bysecond: [], byminute: [], byhour: [], byday: [ICalendarDay { ordwk: None, weekday: Thursday }], bymonthday: [], byyearday: [], byweekno: [], bymonth: [], bysetpos: [], wkst: Some(Sunday), rscale: None, skip: None }) summary.summary: Text("Meet me maybe") ~summary.description: Text("This is the event description") BEGIN:VCALENDAR @@ -179,6 +179,7 @@ METHOD:REQUEST PRODID:-//Stalwart Labs LLC//Stalwart Server//EN VERSION:2.0 BEGIN:VEVENT +X-MICROSOFT-CDO-OWNERAPPTID:299828133 DESCRIPTION:This is the updated event description LOCATION: STATUS:CONFIRMED @@ -186,8 +187,8 @@ SUMMARY:Meet me maybe DTEND;TZID=America/Los_Angeles:20250619T064500 DTSTART;TZID=America/Los_Angeles:20250619T060000 TRANSP:OPAQUE -ATTENDEE;CUTYPE=INDIVIDUAL;ROLE=REQ-PARTICIPANT;PARTSTAT=NEEDS-ACTION;RSVP= - TRUE;CN=b@gmail.com;X-NUM-GUESTS=0:mailto:b@gmail.com +ATTENDEE;CUTYPE=INDIVIDUAL;ROLE=REQ-PARTICIPANT;PARTSTAT=NEEDS-ACTION;RSVP=TRUE; + CN=b@gmail.com;X-NUM-GUESTS=0:mailto:b@gmail.com ATTENDEE;CUTYPE=INDIVIDUAL;ROLE=REQ-PARTICIPANT;PARTSTAT=ACCEPTED;RSVP=TRUE; CN="John Doe";X-NUM-GUESTS=0:mailto:a@gmail.com ORGANIZER;CN="John Doe":mailto:a@gmail.com @@ -197,11 +198,10 @@ CREATED:20250616T182358Z DTSTAMP:0 LAST-MODIFIED:20250616T182415Z SEQUENCE:1 -X-MICROSOFT-CDO-OWNERAPPTID:299828133 END:VEVENT BEGIN:VTIMEZONE -TZID:America/Los_Angeles X-LIC-LOCATION:America/Los_Angeles +TZID:America/Los_Angeles BEGIN:DAYLIGHT DTSTART:19700308T020000 TZNAME:PDT @@ -227,6 +227,7 @@ BEGIN:VCALENDAR PRODID:-//Stalwart Labs LLC//Stalwart Server//EN VERSION:2.0 BEGIN:VEVENT +X-MICROSOFT-CDO-OWNERAPPTID:299828133 DESCRIPTION:This is the updated event description LOCATION: STATUS:CONFIRMED @@ -234,8 +235,8 @@ SUMMARY:Meet me maybe DTEND;TZID=America/Los_Angeles:20250619T064500 DTSTART;TZID=America/Los_Angeles:20250619T060000 TRANSP:OPAQUE -ATTENDEE;CUTYPE=INDIVIDUAL;ROLE=REQ-PARTICIPANT;PARTSTAT=NEEDS-ACTION;RSVP= - TRUE;CN=b@gmail.com;X-NUM-GUESTS=0:mailto:b@gmail.com +ATTENDEE;CUTYPE=INDIVIDUAL;ROLE=REQ-PARTICIPANT;PARTSTAT=NEEDS-ACTION;RSVP=TRUE; + CN=b@gmail.com;X-NUM-GUESTS=0:mailto:b@gmail.com ATTENDEE;CUTYPE=INDIVIDUAL;ROLE=REQ-PARTICIPANT;PARTSTAT=ACCEPTED;RSVP=TRUE; CN="John Doe";X-NUM-GUESTS=0:mailto:a@gmail.com ORGANIZER;CN="John Doe":mailto:a@gmail.com @@ -245,7 +246,6 @@ CREATED:20250616T182358Z DTSTAMP:0 LAST-MODIFIED:20250616T182358Z SEQUENCE:1 -X-MICROSOFT-CDO-OWNERAPPTID:299828133 BEGIN:VALARM DESCRIPTION:This is an event reminder ACTION:DISPLAY @@ -253,8 +253,8 @@ TRIGGER:-PT10M END:VALARM END:VEVENT BEGIN:VTIMEZONE -TZID:America/Los_Angeles X-LIC-LOCATION:America/Los_Angeles +TZID:America/Los_Angeles BEGIN:DAYLIGHT DTSTART:19700308T020000 TZNAME:PDT diff --git a/tests/resources/itip/rfc5546_event_recurring.txt b/tests/resources/itip/rfc5546_event_recurring.txt index 663cdc23..cef668f6 100644 --- a/tests/resources/itip/rfc5546_event_recurring.txt +++ b/tests/resources/itip/rfc5546_event_recurring.txt @@ -33,7 +33,7 @@ summary.attendee: Participants([ItipParticipant { email: "a@example.com", name: summary.description: Text("IETF-C&S Conference Call") summary.dtstart: Time(ItipTime { start: 865198800, tz_id: 32768 }) summary.location: Text("Conference Call") -summary.rrule: Rrule(ICalendarRecurrenceRule { freq: Monthly, until: Some(PartialDateTime { year: Some(1998), month: Some(9), day: Some(1), hour: Some(21), minute: Some(0), second: Some(0), tz_hour: Some(0), tz_minute: Some(0), tz_minus: false }), count: None, interval: None, bysecond: [], byminute: [], byhour: [], byday: [], bymonthday: [1], byyearday: [], byweekno: [], bymonth: [], bysetpos: [], wkst: None }) +summary.rrule: Rrule(ICalendarRecurrenceRule { freq: Monthly, until: Some(PartialDateTime { year: Some(1998), month: Some(9), day: Some(1), hour: Some(21), minute: Some(0), second: Some(0), tz_hour: Some(0), tz_minute: Some(0), tz_minus: false }), count: None, interval: None, bysecond: [], byminute: [], byhour: [], byday: [], bymonthday: [1], byyearday: [], byweekno: [], bymonth: [], bysetpos: [], wkst: None, rscale: None, skip: None }) summary.summary: Text("IETF Calendaring Working Group Meeting") BEGIN:VCALENDAR METHOD:REQUEST @@ -113,7 +113,7 @@ summary.attendee: Participants([ItipParticipant { email: "a@example.com", name: summary.description: Text("IETF-C&S Conference Call") summary.dtstart: Time(ItipTime { start: 865198800, tz_id: 32768 }) summary.location: Text("Conference Call") -summary.rrule: Rrule(ICalendarRecurrenceRule { freq: Monthly, until: Some(PartialDateTime { year: Some(1998), month: Some(9), day: Some(1), hour: Some(21), minute: Some(0), second: Some(0), tz_hour: Some(0), tz_minute: Some(0), tz_minus: false }), count: None, interval: None, bysecond: [], byminute: [], byhour: [], byday: [], bymonthday: [1], byyearday: [], byweekno: [], bymonth: [], bysetpos: [], wkst: None }) +summary.rrule: Rrule(ICalendarRecurrenceRule { freq: Monthly, until: Some(PartialDateTime { year: Some(1998), month: Some(9), day: Some(1), hour: Some(21), minute: Some(0), second: Some(0), tz_hour: Some(0), tz_minute: Some(0), tz_minus: false }), count: None, interval: None, bysecond: [], byminute: [], byhour: [], byday: [], bymonthday: [1], byyearday: [], byweekno: [], bymonth: [], bysetpos: [], wkst: None, rscale: None, skip: None }) summary.summary: Text("IETF Calendaring Working Group Meeting") BEGIN:VCALENDAR METHOD:ADD @@ -203,7 +203,7 @@ summary.attendee: Participants([ItipParticipant { email: "a@example.com", name: summary.description: Text("IETF-C&S Conference Call") summary.dtstart: Time(ItipTime { start: 865198800, tz_id: 32768 }) summary.location: Text("Conference Call") -summary.rrule: Rrule(ICalendarRecurrenceRule { freq: Monthly, until: Some(PartialDateTime { year: Some(1998), month: Some(9), day: Some(1), hour: Some(21), minute: Some(0), second: Some(0), tz_hour: Some(0), tz_minute: Some(0), tz_minus: false }), count: None, interval: None, bysecond: [], byminute: [], byhour: [], byday: [], bymonthday: [1], byyearday: [], byweekno: [], bymonth: [], bysetpos: [], wkst: None }) +summary.rrule: Rrule(ICalendarRecurrenceRule { freq: Monthly, until: Some(PartialDateTime { year: Some(1998), month: Some(9), day: Some(1), hour: Some(21), minute: Some(0), second: Some(0), tz_hour: Some(0), tz_minute: Some(0), tz_minus: false }), count: None, interval: None, bysecond: [], byminute: [], byhour: [], byday: [], bymonthday: [1], byyearday: [], byweekno: [], bymonth: [], bysetpos: [], wkst: None, rscale: None, skip: None }) summary.summary: Text("IETF Calendaring Working Group Meeting") BEGIN:VCALENDAR METHOD:CANCEL @@ -354,7 +354,7 @@ summary.attendee: Participants([ItipParticipant { email: "a@example.com", name: summary.description: Text("IETF-C&S Conference Call") summary.dtstart: Time(ItipTime { start: 865198800, tz_id: 32768 }) summary.location: Text("Conference Call") -summary.rrule: Rrule(ICalendarRecurrenceRule { freq: Monthly, until: Some(PartialDateTime { year: Some(1998), month: Some(9), day: Some(1), hour: Some(21), minute: Some(0), second: Some(0), tz_hour: Some(0), tz_minute: Some(0), tz_minus: false }), count: None, interval: None, bysecond: [], byminute: [], byhour: [], byday: [], bymonthday: [1], byyearday: [], byweekno: [], bymonth: [], bysetpos: [], wkst: None }) +summary.rrule: Rrule(ICalendarRecurrenceRule { freq: Monthly, until: Some(PartialDateTime { year: Some(1998), month: Some(9), day: Some(1), hour: Some(21), minute: Some(0), second: Some(0), tz_hour: Some(0), tz_minute: Some(0), tz_minus: false }), count: None, interval: None, bysecond: [], byminute: [], byhour: [], byday: [], bymonthday: [1], byyearday: [], byweekno: [], bymonth: [], bysetpos: [], wkst: None, rscale: None, skip: None }) summary.summary: Text("IETF Calendaring Working Group Meeting") BEGIN:VCALENDAR METHOD:ADD @@ -534,7 +534,7 @@ summary: cancel summary.description: Text("IETF-C&S Conference Call") summary.dtstart: Time(ItipTime { start: 865198800, tz_id: 32768 }) summary.location: Text("Conference Call") -summary.rrule: Rrule(ICalendarRecurrenceRule { freq: Monthly, until: Some(PartialDateTime { year: Some(1998), month: Some(9), day: Some(1), hour: Some(21), minute: Some(0), second: Some(0), tz_hour: Some(0), tz_minute: Some(0), tz_minus: false }), count: None, interval: None, bysecond: [], byminute: [], byhour: [], byday: [], bymonthday: [1], byyearday: [], byweekno: [], bymonth: [], bysetpos: [], wkst: None }) +summary.rrule: Rrule(ICalendarRecurrenceRule { freq: Monthly, until: Some(PartialDateTime { year: Some(1998), month: Some(9), day: Some(1), hour: Some(21), minute: Some(0), second: Some(0), tz_hour: Some(0), tz_minute: Some(0), tz_minus: false }), count: None, interval: None, bysecond: [], byminute: [], byhour: [], byday: [], bymonthday: [1], byyearday: [], byweekno: [], bymonth: [], bysetpos: [], wkst: None, rscale: None, skip: None }) summary.summary: Text("IETF Calendaring Working Group Meeting") BEGIN:VCALENDAR METHOD:CANCEL @@ -673,7 +673,7 @@ summary: invite summary.attendee: Participants([ItipParticipant { email: "a@example.com", name: None, is_organizer: true }, ItipParticipant { email: "b@example.com", name: None, is_organizer: false }]) summary.dtstart: Time(ItipTime { start: 888958800, tz_id: 32768 }) summary.location: Text("The White Room") -summary.rrule: Rrule(ICalendarRecurrenceRule { freq: Weekly, until: None, count: None, interval: None, bysecond: [], byminute: [], byhour: [], byday: [ICalendarDay { ordwk: None, weekday: Tuesday }], bymonthday: [], byyearday: [], byweekno: [], bymonth: [], bysetpos: [], wkst: Some(Sunday) }) +summary.rrule: Rrule(ICalendarRecurrenceRule { freq: Weekly, until: None, count: None, interval: None, bysecond: [], byminute: [], byhour: [], byday: [ICalendarDay { ordwk: None, weekday: Tuesday }], bymonthday: [], byyearday: [], byweekno: [], bymonth: [], bysetpos: [], wkst: Some(Sunday), rscale: None, skip: None }) summary.summary: Text("Review Accounts") BEGIN:VCALENDAR METHOD:REQUEST @@ -737,9 +737,9 @@ summary: update REQUEST summary.attendee: Participants([ItipParticipant { email: "a@example.com", name: None, is_organizer: true }, ItipParticipant { email: "b@example.com", name: None, is_organizer: false }]) summary.dtstart: Time(ItipTime { start: 888958800, tz_id: 32768 }) summary.location: Text("The White Room") -summary.rrule: Rrule(ICalendarRecurrenceRule { freq: Weekly, until: None, count: None, interval: None, bysecond: [], byminute: [], byhour: [], byday: [ICalendarDay { ordwk: None, weekday: Tuesday }, ICalendarDay { ordwk: None, weekday: Thursday }], bymonthday: [], byyearday: [], byweekno: [], bymonth: [], bysetpos: [], wkst: Some(Sunday) }) +summary.rrule: Rrule(ICalendarRecurrenceRule { freq: Weekly, until: None, count: None, interval: None, bysecond: [], byminute: [], byhour: [], byday: [ICalendarDay { ordwk: None, weekday: Tuesday }, ICalendarDay { ordwk: None, weekday: Thursday }], bymonthday: [], byyearday: [], byweekno: [], bymonth: [], bysetpos: [], wkst: Some(Sunday), rscale: None, skip: None }) summary.summary: Text("Review Accounts") -~summary.rrule: Rrule(ICalendarRecurrenceRule { freq: Weekly, until: None, count: None, interval: None, bysecond: [], byminute: [], byhour: [], byday: [ICalendarDay { ordwk: None, weekday: Tuesday }], bymonthday: [], byyearday: [], byweekno: [], bymonth: [], bysetpos: [], wkst: Some(Sunday) }) +~summary.rrule: Rrule(ICalendarRecurrenceRule { freq: Weekly, until: None, count: None, interval: None, bysecond: [], byminute: [], byhour: [], byday: [ICalendarDay { ordwk: None, weekday: Tuesday }], bymonthday: [], byyearday: [], byweekno: [], bymonth: [], bysetpos: [], wkst: Some(Sunday), rscale: None, skip: None }) BEGIN:VCALENDAR METHOD:REQUEST PRODID:-//Stalwart Labs LLC//Stalwart Server//EN @@ -766,7 +766,7 @@ summary: cancel summary.attendee: Participants([ItipParticipant { email: "a@example.com", name: None, is_organizer: true }, ItipParticipant { email: "b@example.com", name: None, is_organizer: false }]) summary.dtstart: Time(ItipTime { start: 888958800, tz_id: 32768 }) summary.location: Text("The White Room") -summary.rrule: Rrule(ICalendarRecurrenceRule { freq: Weekly, until: None, count: None, interval: None, bysecond: [], byminute: [], byhour: [], byday: [ICalendarDay { ordwk: None, weekday: Tuesday }], bymonthday: [], byyearday: [], byweekno: [], bymonth: [], bysetpos: [], wkst: Some(Sunday) }) +summary.rrule: Rrule(ICalendarRecurrenceRule { freq: Weekly, until: None, count: None, interval: None, bysecond: [], byminute: [], byhour: [], byday: [ICalendarDay { ordwk: None, weekday: Tuesday }], bymonthday: [], byyearday: [], byweekno: [], bymonth: [], bysetpos: [], wkst: Some(Sunday), rscale: None, skip: None }) summary.summary: Text("Review Accounts") BEGIN:VCALENDAR METHOD:CANCEL @@ -849,7 +849,7 @@ to: a@example.com summary: rsvp DECLINED summary.dtstart: Time(ItipTime { start: 888958800, tz_id: 32768 }) summary.location: Text("The White Room") -summary.rrule: Rrule(ICalendarRecurrenceRule { freq: Weekly, until: None, count: None, interval: None, bysecond: [], byminute: [], byhour: [], byday: [ICalendarDay { ordwk: None, weekday: Tuesday }, ICalendarDay { ordwk: None, weekday: Thursday }], bymonthday: [], byyearday: [], byweekno: [], bymonth: [], bysetpos: [], wkst: Some(Sunday) }) +summary.rrule: Rrule(ICalendarRecurrenceRule { freq: Weekly, until: None, count: None, interval: None, bysecond: [], byminute: [], byhour: [], byday: [ICalendarDay { ordwk: None, weekday: Tuesday }, ICalendarDay { ordwk: None, weekday: Thursday }], bymonthday: [], byyearday: [], byweekno: [], bymonth: [], bysetpos: [], wkst: Some(Sunday), rscale: None, skip: None }) summary.summary: Text("Review Accounts") BEGIN:VCALENDAR METHOD:REPLY diff --git a/tests/resources/itip/rfc5546_event_single.txt b/tests/resources/itip/rfc5546_event_single.txt index aaab7981..fd9ac02e 100644 --- a/tests/resources/itip/rfc5546_event_single.txt +++ b/tests/resources/itip/rfc5546_event_single.txt @@ -289,10 +289,9 @@ BEGIN:VEVENT SUMMARY:Phone Conference DTEND:19970701T190000Z DTSTART:19970701T180000Z -ATTENDEE;PARTSTAT=DELEGATED;DELEGATED-TO="mailto:e@example.com":mailto:c@ex - ample.com -ATTENDEE;RSVP=TRUE;DELEGATED-FROM="mailto:c@example.com":mailto:e@example.c - om +ATTENDEE;PARTSTAT=DELEGATED;DELEGATED-TO="mailto:e@example.com":mailto:c@examp + le.com +ATTENDEE;RSVP=TRUE;DELEGATED-FROM="mailto:c@example.com":mailto:e@example.com ORGANIZER:mailto:a@example.com UID:calsrv.example.com-873970198738777@example.com DTSTAMP:0 @@ -316,16 +315,16 @@ STATUS:CONFIRMED SUMMARY:Phone Conference DTEND:19970701T190000Z DTSTART:19970701T180000Z -ATTENDEE;PARTSTAT=DELEGATED;DELEGATED-TO="mailto:e@example.com":mailto:c@ex - ample.com +ATTENDEE;PARTSTAT=DELEGATED;DELEGATED-TO="mailto:e@example.com":mailto:c@examp + le.com ATTENDEE;ROLE=CHAIR;PARTSTAT=ACCEPTED:mailto:a@example.com ATTENDEE;ROLE=NON-PARTICIPANT;RSVP=FALSE;CUTYPE=ROOM:mailto:conf@example.com ATTENDEE;RSVP=TRUE;CUTYPE=INDIVIDUAL;CN=Hal;PARTSTAT=NEEDS-ACTION:mailto:d@e xample.com ATTENDEE;RSVP=TRUE;CUTYPE=INDIVIDUAL;PARTSTAT=NEEDS-ACTION:mailto:b@example. com -ATTENDEE;RSVP=TRUE;DELEGATED-FROM="mailto:c@example.com";PARTSTAT=NEEDS-AC - TION:mailto:e@example.com +ATTENDEE;RSVP=TRUE;DELEGATED-FROM="mailto:c@example.com";PARTSTAT=NEEDS-ACTIO + N:mailto:e@example.com ORGANIZER:mailto:a@example.com UID:calsrv.example.com-873970198738777@example.com DTSTAMP:0 @@ -428,10 +427,10 @@ BEGIN:VEVENT SUMMARY:Phone Conference DTEND:19970701T190000Z DTSTART:19970701T180000Z -ATTENDEE;PARTSTAT=DELEGATED;DELEGATED-TO="mailto:e@example.com":mailto:c@ex - ample.com -ATTENDEE;RSVP=TRUE;DELEGATED-FROM="mailto:c@example.com";PARTSTAT=ACCEPTED: - mailto:e@example.com +ATTENDEE;PARTSTAT=DELEGATED;DELEGATED-TO="mailto:e@example.com":mailto:c@examp + le.com +ATTENDEE;RSVP=TRUE;DELEGATED-FROM="mailto:c@example.com";PARTSTAT=ACCEPTED:mai + lto:e@example.com ORGANIZER:mailto:a@example.com UID:calsrv.example.com-873970198738777@example.com DTSTAMP:0 @@ -535,10 +534,10 @@ BEGIN:VEVENT SUMMARY:Phone Conference DTEND:19970701T190000Z DTSTART:19970701T180000Z -ATTENDEE;PARTSTAT=DELEGATED;DELEGATED-TO="mailto:e@example.com":mailto:c@ex - ample.com -ATTENDEE;RSVP=TRUE;DELEGATED-FROM="mailto:c@example.com";PARTSTAT=DECLINED: - mailto:e@example.com +ATTENDEE;PARTSTAT=DELEGATED;DELEGATED-TO="mailto:e@example.com":mailto:c@examp + le.com +ATTENDEE;RSVP=TRUE;DELEGATED-FROM="mailto:c@example.com";PARTSTAT=DECLINED:mai + lto:e@example.com ORGANIZER:mailto:a@example.com UID:calsrv.example.com-873970198738777@example.com DTSTAMP:0 @@ -643,14 +642,14 @@ STATUS:CONFIRMED SUMMARY:Phone Conference DTEND:19970701T190000Z DTSTART:19970701T180000Z -ATTENDEE;CUTYPE=INDIVIDUAL;PARTSTAT=DELEGATED;DELEGATED-TO="mailto:e@exam - ple.com":mailto:c@example.com +ATTENDEE;CUTYPE=INDIVIDUAL;PARTSTAT=DELEGATED;DELEGATED-TO="mailto:e@example + .com":mailto:c@example.com ATTENDEE;ROLE=CHAIR;PARTSTAT=ACCEPTED:mailto:a@example.com ATTENDEE;ROLE=NON-PARTICIPANT;RSVP=FALSE;CUTYPE=ROOM:mailto:conf@example.com ATTENDEE;RSVP=TRUE;CUTYPE=INDIVIDUAL;CN=Hal;PARTSTAT=NEEDS-ACTION:mailto:d@e xample.com -ATTENDEE;RSVP=TRUE;DELEGATED-FROM="mailto:c@example.com";PARTSTAT=DECLINED: - mailto:e@example.com +ATTENDEE;RSVP=TRUE;DELEGATED-FROM="mailto:c@example.com";PARTSTAT=DECLINED:mai + lto:e@example.com ORGANIZER:mailto:a@example.com UID:calsrv.example.com-873970198738777@example.com DTSTAMP:0 @@ -719,8 +718,8 @@ STATUS:CANCELLED SUMMARY:Phone Conference DTEND:19970701T190000Z DTSTART:19970701T180000Z -ATTENDEE;CUTYPE=INDIVIDUAL;PARTSTAT=DELEGATED;DELEGATED-TO="mailto:e@exam - ple.com";SCHEDULE-STATUS=2.0:mailto:c@example.com +ATTENDEE;CUTYPE=INDIVIDUAL;PARTSTAT=DELEGATED;DELEGATED-TO="mailto:e@example + .com";SCHEDULE-STATUS=2.0:mailto:c@example.com ATTENDEE;ROLE=CHAIR;PARTSTAT=ACCEPTED:mailto:a@example.com ATTENDEE;ROLE=NON-PARTICIPANT;RSVP=FALSE;CUTYPE=ROOM:mailto:conf@example.com ATTENDEE;RSVP=TRUE;CUTYPE=INDIVIDUAL;CN=Hal:mailto:d@example.com diff --git a/tests/resources/itip/rfc5546_todo.txt b/tests/resources/itip/rfc5546_todo.txt index 8abde70e..f296fc7c 100644 --- a/tests/resources/itip/rfc5546_todo.txt +++ b/tests/resources/itip/rfc5546_todo.txt @@ -310,7 +310,7 @@ to: b@example.com, d@example.com summary: update REQUEST summary.attendee: Participants([ItipParticipant { email: "a@example.com", name: None, is_organizer: true }, ItipParticipant { email: "b@example.com", name: None, is_organizer: false }, ItipParticipant { email: "d@example.com", name: None, is_organizer: false }]) summary.dtstart: Time(ItipTime { start: 883648800, tz_id: 32768 }) -summary.rrule: Rrule(ICalendarRecurrenceRule { freq: Monthly, until: None, count: Some(10), interval: None, bysecond: [], byminute: [], byhour: [], byday: [ICalendarDay { ordwk: Some(1), weekday: Friday }], bymonthday: [], byyearday: [], byweekno: [], bymonth: [], bysetpos: [], wkst: None }) +summary.rrule: Rrule(ICalendarRecurrenceRule { freq: Monthly, until: None, count: Some(10), interval: None, bysecond: [], byminute: [], byhour: [], byday: [ICalendarDay { ordwk: Some(1), weekday: Friday }], bymonthday: [], byyearday: [], byweekno: [], bymonth: [], bysetpos: [], wkst: None, rscale: None, skip: None }) summary.summary: Text("Send Status Reports to Area Managers") ~summary.attendee: Participants([ItipParticipant { email: "a@example.com", name: None, is_organizer: true }, ItipParticipant { email: "b@example.com", name: None, is_organizer: false }, ItipParticipant { email: "c@example.com", name: None, is_organizer: false }, ItipParticipant { email: "d@example.com", name: None, is_organizer: false }]) ~summary.dtstart: Time(ItipTime { start: 867776400, tz_id: 32768 }) @@ -343,7 +343,7 @@ to: c@example.com summary: cancel summary.attendee: Participants([ItipParticipant { email: "a@example.com", name: None, is_organizer: true }, ItipParticipant { email: "b@example.com", name: None, is_organizer: false }, ItipParticipant { email: "c@example.com", name: None, is_organizer: false }, ItipParticipant { email: "d@example.com", name: None, is_organizer: false }]) summary.dtstart: Time(ItipTime { start: 867776400, tz_id: 32768 }) -summary.rrule: Rrule(ICalendarRecurrenceRule { freq: Monthly, until: None, count: Some(10), interval: None, bysecond: [], byminute: [], byhour: [], byday: [ICalendarDay { ordwk: Some(1), weekday: Friday }], bymonthday: [], byyearday: [], byweekno: [], bymonth: [], bysetpos: [], wkst: None }) +summary.rrule: Rrule(ICalendarRecurrenceRule { freq: Monthly, until: None, count: Some(10), interval: None, bysecond: [], byminute: [], byhour: [], byday: [ICalendarDay { ordwk: Some(1), weekday: Friday }], bymonthday: [], byyearday: [], byweekno: [], bymonth: [], bysetpos: [], wkst: None, rscale: None, skip: None }) summary.summary: Text("Create the requirements document") BEGIN:VCALENDAR METHOD:CANCEL @@ -447,7 +447,7 @@ to: a@example.com summary: rsvp NEEDS-ACTION summary.attendee: Participants([ItipParticipant { email: "a@example.com", name: None, is_organizer: true }, ItipParticipant { email: "b@example.com", name: None, is_organizer: false }, ItipParticipant { email: "d@example.com", name: None, is_organizer: false }]) summary.dtstart: Time(ItipTime { start: 883648800, tz_id: 32768 }) -summary.rrule: Rrule(ICalendarRecurrenceRule { freq: Monthly, until: None, count: Some(10), interval: None, bysecond: [], byminute: [], byhour: [], byday: [ICalendarDay { ordwk: Some(1), weekday: Friday }], bymonthday: [], byyearday: [], byweekno: [], bymonth: [], bysetpos: [], wkst: None }) +summary.rrule: Rrule(ICalendarRecurrenceRule { freq: Monthly, until: None, count: Some(10), interval: None, bysecond: [], byminute: [], byhour: [], byday: [ICalendarDay { ordwk: Some(1), weekday: Friday }], bymonthday: [], byyearday: [], byweekno: [], bymonth: [], bysetpos: [], wkst: None, rscale: None, skip: None }) summary.summary: Text("Send Status Reports to Area Managers") BEGIN:VCALENDAR METHOD:REPLY diff --git a/tests/resources/itip/rfc6638_recurring.txt b/tests/resources/itip/rfc6638_recurring.txt index 7627b74f..ddeebddc 100644 --- a/tests/resources/itip/rfc6638_recurring.txt +++ b/tests/resources/itip/rfc6638_recurring.txt @@ -43,7 +43,7 @@ to: bernard@example.net summary: invite summary.attendee: Participants([ItipParticipant { email: "cyrus@example.com", name: Some("Cyrus Daboo"), is_organizer: true }, ItipParticipant { email: "bernard@example.net", name: Some("Bernard Desruisseaux"), is_organizer: false }]) summary.dtstart: Time(ItipTime { start: 1243882800, tz_id: 167 }) -summary.rrule: Rrule(ICalendarRecurrenceRule { freq: Daily, until: None, count: Some(5), interval: Some(1), bysecond: [], byminute: [], byhour: [], byday: [], bymonthday: [], byyearday: [], byweekno: [], bymonth: [], bysetpos: [], wkst: None }) +summary.rrule: Rrule(ICalendarRecurrenceRule { freq: Daily, until: None, count: Some(5), interval: Some(1), bysecond: [], byminute: [], byhour: [], byday: [], bymonthday: [], byyearday: [], byweekno: [], bymonth: [], bysetpos: [], wkst: None, rscale: None, skip: None }) summary.summary: Text("Review Internet-Draft") BEGIN:VCALENDAR METHOD:REQUEST @@ -54,8 +54,8 @@ SUMMARY:Review Internet-Draft DTEND;TZID=America/Montreal:20090601T160000 DTSTART;TZID=America/Montreal:20090601T150000 TRANSP:OPAQUE -ATTENDEE;CN="Bernard Desruisseaux";CUTYPE=INDIVIDUAL;ROLE=REQ-PARTICIPANT;RSVP= - TRUE;PARTSTAT=NEEDS-ACTION:mailto:bernard@example.net +ATTENDEE;CN="Bernard Desruisseaux";CUTYPE=INDIVIDUAL;ROLE=REQ-PARTICIPANT; + RSVP=TRUE;PARTSTAT=NEEDS-ACTION:mailto:bernard@example.net ATTENDEE;CN="Cyrus Daboo";CUTYPE=INDIVIDUAL;PARTSTAT=ACCEPTED:mailto:cyrus@e xample.com ORGANIZER;CN="Cyrus Daboo":mailto:cyrus@example.com @@ -181,7 +181,7 @@ to: cyrus@example.com summary: rsvp ACCEPTED summary.attendee: Participants([ItipParticipant { email: "cyrus@example.com", name: Some("Cyrus Daboo"), is_organizer: true }, ItipParticipant { email: "bernard@example.net", name: Some("Bernard Desruisseaux"), is_organizer: false }]) summary.dtstart: Time(ItipTime { start: 1243882800, tz_id: 167 }) -summary.rrule: Rrule(ICalendarRecurrenceRule { freq: Daily, until: None, count: Some(5), interval: Some(1), bysecond: [], byminute: [], byhour: [], byday: [], bymonthday: [], byyearday: [], byweekno: [], bymonth: [], bysetpos: [], wkst: None }) +summary.rrule: Rrule(ICalendarRecurrenceRule { freq: Daily, until: None, count: Some(5), interval: Some(1), bysecond: [], byminute: [], byhour: [], byday: [], bymonthday: [], byyearday: [], byweekno: [], bymonth: [], bysetpos: [], wkst: None, rscale: None, skip: None }) summary.summary: Text("Review Internet-Draft") BEGIN:VCALENDAR METHOD:REPLY @@ -191,8 +191,8 @@ BEGIN:VEVENT SUMMARY:Review Internet-Draft DTEND;TZID=America/Montreal:20090601T160000 DTSTART;TZID=America/Montreal:20090601T150000 -ATTENDEE;CN="Bernard Desruisseaux";CUTYPE=INDIVIDUAL;PARTSTAT=ACCEPTED;ROLE= - REQ-PARTICIPANT;RSVP=TRUE:mailto:bernard@example.net +ATTENDEE;CN="Bernard Desruisseaux";CUTYPE=INDIVIDUAL;PARTSTAT=ACCEPTED; + ROLE=REQ-PARTICIPANT;RSVP=TRUE:mailto:bernard@example.net ORGANIZER;CN="Cyrus Daboo":mailto:cyrus@example.com UID:9263504FD3AD DTSTAMP:0 @@ -203,8 +203,8 @@ BEGIN:VEVENT SUMMARY:Review Internet-Draft DTEND;TZID=America/Montreal:20090602T160000 DTSTART;TZID=America/Montreal:20090602T150000 -ATTENDEE;CN="Bernard Desruisseaux";CUTYPE=INDIVIDUAL;PARTSTAT=DECLINED;ROLE= - REQ-PARTICIPANT;RSVP=TRUE:mailto:bernard@example.net +ATTENDEE;CN="Bernard Desruisseaux";CUTYPE=INDIVIDUAL;PARTSTAT=DECLINED; + ROLE=REQ-PARTICIPANT;RSVP=TRUE:mailto:bernard@example.net ORGANIZER;CN="Cyrus Daboo":mailto:cyrus@example.com RECURRENCE-ID;TZID=America/Montreal:20090602T150000 UID:9263504FD3AD @@ -339,7 +339,7 @@ to: cyrus@example.com summary: rsvp DECLINED summary.attendee: Participants([ItipParticipant { email: "cyrus@example.com", name: Some("Cyrus Daboo"), is_organizer: true }, ItipParticipant { email: "bernard@example.net", name: Some("Bernard Desruisseaux"), is_organizer: false }]) summary.dtstart: Time(ItipTime { start: 1243882800, tz_id: 167 }) -summary.rrule: Rrule(ICalendarRecurrenceRule { freq: Daily, until: None, count: Some(5), interval: Some(1), bysecond: [], byminute: [], byhour: [], byday: [], bymonthday: [], byyearday: [], byweekno: [], bymonth: [], bysetpos: [], wkst: None }) +summary.rrule: Rrule(ICalendarRecurrenceRule { freq: Daily, until: None, count: Some(5), interval: Some(1), bysecond: [], byminute: [], byhour: [], byday: [], bymonthday: [], byyearday: [], byweekno: [], bymonth: [], bysetpos: [], wkst: None, rscale: None, skip: None }) summary.summary: Text("Review Internet-Draft") BEGIN:VCALENDAR METHOD:REPLY diff --git a/tests/resources/itip/rfc6638_single.txt b/tests/resources/itip/rfc6638_single.txt index 91468201..64d12f88 100644 --- a/tests/resources/itip/rfc6638_single.txt +++ b/tests/resources/itip/rfc6638_single.txt @@ -176,8 +176,8 @@ BEGIN:VEVENT SUMMARY:Lunch DTEND:20090602T170000Z DTSTART:20090602T160000Z -ATTENDEE;CN="Wilfredo Sanchez Vega";CUTYPE=INDIVIDUAL;PARTSTAT=ACCEPTED;ROLE= - REQ-PARTICIPANT;RSVP=TRUE:mailto:wilfredo@example.com +ATTENDEE;CN="Wilfredo Sanchez Vega";CUTYPE=INDIVIDUAL;PARTSTAT=ACCEPTED; + ROLE=REQ-PARTICIPANT;RSVP=TRUE:mailto:wilfredo@example.com ORGANIZER;CN="Cyrus Daboo":mailto:cyrus@example.com UID:9263504FD3AD DTSTAMP:0 diff --git a/tests/src/webdav/cal_itip.rs b/tests/src/webdav/cal_itip.rs index d57b15ce..0c83d4b0 100644 --- a/tests/src/webdav/cal_itip.rs +++ b/tests/src/webdav/cal_itip.rs @@ -6,7 +6,7 @@ use ahash::AHashMap; use calcard::{ - common::PartialDateTime, + common::{IanaString, PartialDateTime}, icalendar::{ICalendar, ICalendarProperty, ICalendarValue}, }; use groupware::scheduling::{ @@ -422,11 +422,11 @@ fn normalize_ical(mut ical: ICalendar, map: &mut AHashMap>(); comps.sort_unstable_by_key(|(_, comp)| *comp); - ical.components[0].component_ids = comps.iter().map(|(comp_id, _)| *comp_id as u16).collect(); + ical.components[0].component_ids = comps.iter().map(|(comp_id, _)| *comp_id as u32).collect(); for comp in &mut ical.components { for entry in &mut comp.entries { diff --git a/tests/src/webdav/cal_query.rs b/tests/src/webdav/cal_query.rs index 933e06f0..9123a73f 100644 --- a/tests/src/webdav/cal_query.rs +++ b/tests/src/webdav/cal_query.rs @@ -732,7 +732,7 @@ BEGIN:VFREEBUSY DTSTART:20060104T140000Z DTEND:20060105T220000Z FREEBUSY;FBTYPE=BUSY-TENTATIVE:20060104T150000Z/20060104T160000Z -FREEBUSY;FBTYPE=BUSY:20060104T190000Z/20060104T200000Z;20060105T170000Z/20060105T180000Z +FREEBUSY;FBTYPE=BUSY:20060104T190000Z/20060104T200000Z,20060105T170000Z/20060105T180000Z FREEBUSY;FBTYPE=BUSY-UNAVAILABLE:20060105T100000Z/20060105T120000Z END:VFREEBUSY END:VCALENDAR @@ -753,8 +753,8 @@ DTSTART:20060101T000000Z DTEND:20060104T140000Z DTSTAMP:20250505T105255Z FREEBUSY;FBTYPE=BUSY-TENTATIVE:20060102T100000Z/20060102T120000Z -FREEBUSY;FBTYPE=BUSY:20060102T150000Z/20060102T160000Z;20060102T170000Z/20060102T180000Z; - 20060103T100000Z/20060103T120000Z;20060103T170000Z/20060103T180000Z;20060104T100000Z/20060104T120000Z +FREEBUSY;FBTYPE=BUSY:20060102T150000Z/20060102T160000Z,20060102T170000Z/20060102T180000Z, + 20060103T100000Z/20060103T120000Z,20060103T170000Z/20060103T180000Z,20060104T100000Z/20060104T120000Z END:VFREEBUSY END:VCALENDAR "#; diff --git a/tests/src/webdav/card_query.rs b/tests/src/webdav/card_query.rs index e0f21a0d..0724f846 100644 --- a/tests/src/webdav/card_query.rs +++ b/tests/src/webdav/card_query.rs @@ -316,7 +316,7 @@ CATEGORIES:Technology,B2B,Solutions,Services NOTE:Business hours: Mon-Fri 9:00-17:30 GMT. Closed on UK bank holidays. VAT Reg: GB123456789 TZ:Z -GEO:51.5074\;-0.1278 +GEO:51.5074;-0.1278 KEY;TYPE=PGP:https://pgp.example.com/pks/lookup?op=get&search=info@acme-solu tions.example UID:urn:uuid:a9e95948-7b1c-46e8-bd85-c729a9e910f2