Bump to calcard 0.2

This commit is contained in:
mdecimus 2025-09-30 18:05:20 +02:00
parent 517ee92317
commit b906eea62d
36 changed files with 284 additions and 216 deletions

8
Cargo.lock generated
View file

@ -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",
]

View file

@ -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"

View file

@ -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"] }

View file

@ -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()

View file

@ -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"

View file

@ -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),
) = (&param.name, &param.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),
});
}

View file

@ -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::<AHashMap<_, _>>();
!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::<AHashSet<_>>();
!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<String> {
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)
}

View file

@ -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);

View file

@ -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(

View file

@ -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"] }

View file

@ -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 (&param.name, &param.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();
}
_ => {}

View file

@ -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()

View file

@ -21,7 +21,7 @@ impl ArchivedCalendarEventData {
let instances = range.instances.as_ref();
let (offset_or_count, bytes_read) = instances.read_leb128::<u32>()?;
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())?;

View file

@ -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),
) = (&param.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;

View file

@ -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<DavName>,
pub display_name: Option<String>,

View file

@ -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()),

View file

@ -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);

View file

@ -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::<Vec<_>>(),
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<MergeAction>) {
let mut remove_component_ids = Vec::new();
let mut remove_component_ids: Vec<u32> = Vec::new();
for action in changes {
match action {
MergeAction::AddEntries {
@ -575,10 +575,10 @@ pub fn itip_merge_changes(ical: &mut ICalendar, changes: Vec<MergeAction>) {
} => {
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(&param.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>) {
}
}
MergeAction::RemoveComponent { component_id } => {
remove_component_ids.push(component_id);
remove_component_ids.push(component_id as u32);
}
}
}

View file

@ -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(_)
&param.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
{

View file

@ -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()))],
}

View file

@ -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();

View file

@ -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 (&param.name, &param.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 (&param.name, &param.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 (&param.name, &param.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,

View file

@ -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"

View file

@ -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"

View file

@ -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!(
"{}: {} @ {}",

View file

@ -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<String> = months.iter().map(|&month| self.month_name(month)).collect();
fn format_months(&self, months: &[ArchivedICalendarMonth]) -> String {
let month_names: Vec<String> = months
.iter()
.map(|month| self.month_name(month.month()))
.collect();
format!("in {}", self.format_list(&month_names))
}

View file

@ -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" }

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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<PartialDateTime, usize
.filter(|(comp_id, _)| {
ical.components[0]
.component_ids
.contains(&(*comp_id as u16))
.contains(&(*comp_id as u32))
})
.collect::<Vec<_>>();
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 {

View file

@ -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
"#;

View file

@ -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