ITIP: Add organizer to iMIP replies if missing to deal with MS Exchange 2010 bug

This commit is contained in:
mdecimus 2025-08-26 10:46:37 +02:00
parent 7395f76bba
commit 80633c3441
2 changed files with 43 additions and 3 deletions

View file

@ -328,6 +328,7 @@ impl EmailIngest for Server {
params.access_token, params.access_token,
&resource_token, &resource_token,
sender, sender,
deliver_to,
itip_message, itip_message,
) )
.await .await

View file

@ -19,8 +19,8 @@ use crate::{
use calcard::{ use calcard::{
common::timezone::Tz, common::timezone::Tz,
icalendar::{ icalendar::{
ICalendar, ICalendarComponentType, ICalendarMethod, ICalendarParameter, ICalendar, ICalendarComponentType, ICalendarEntry, ICalendarMethod, ICalendarParameter,
ICalendarParticipationStatus, ICalendarProperty, ICalendarParticipationStatus, ICalendarProperty, ICalendarValue,
}, },
}; };
use common::{ use common::{
@ -52,6 +52,7 @@ pub trait ItipIngest: Sync + Send {
access_token: &AccessToken, access_token: &AccessToken,
resource_token: &ResourceToken, resource_token: &ResourceToken,
sender: &str, sender: &str,
recipient: &str,
itip_message: &str, itip_message: &str,
) -> impl Future<Output = Result<Option<ItipMessage<ICalendar>>, ItipIngestError>> + Send; ) -> impl Future<Output = Result<Option<ItipMessage<ICalendar>>, ItipIngestError>> + Send;
@ -75,10 +76,11 @@ impl ItipIngest for Server {
access_token: &AccessToken, access_token: &AccessToken,
resource_token: &ResourceToken, resource_token: &ResourceToken,
sender: &str, sender: &str,
recipient: &str,
itip_message: &str, itip_message: &str,
) -> Result<Option<ItipMessage<ICalendar>>, ItipIngestError> { ) -> Result<Option<ItipMessage<ICalendar>>, ItipIngestError> {
// Parse and validate the iTIP message // Parse and validate the iTIP message
let itip = ICalendar::parse(itip_message) let mut itip = ICalendar::parse(itip_message)
.map_err(|_| ItipIngestError::Message(ItipError::ICalendarParseError)) .map_err(|_| ItipIngestError::Message(ItipError::ICalendarParseError))
.and_then(|ical| { .and_then(|ical| {
if ical.components.len() > 1 if ical.components.len() > 1
@ -89,6 +91,43 @@ impl ItipIngest for Server {
Err(ItipIngestError::Message(ItipError::ICalendarParseError)) Err(ItipIngestError::Message(ItipError::ICalendarParseError))
} }
})?; })?;
// Microsoft Exchange does not include the organizer in REPLY, assume it is the recipient.
// This will be validated against the stored event anyway.
if itip.components[0]
.property(&ICalendarProperty::Method)
.and_then(|v| v.values.first())
.is_some_and(|v| {
matches!(
v,
ICalendarValue::Method(ICalendarMethod::Reply | ICalendarMethod::Request)
)
})
{
for comp in &mut itip.components {
if comp.component_type.is_scheduling_object() {
let mut has_organizer = false;
let mut has_attendee = false;
for entry in &comp.entries {
match entry.name {
ICalendarProperty::Organizer => has_organizer = true,
ICalendarProperty::Attendee => has_attendee = true,
_ => {}
}
}
if has_attendee && !has_organizer {
comp.entries.push(ICalendarEntry {
name: ICalendarProperty::Organizer,
params: vec![],
values: vec![ICalendarValue::Text(format!("mailto:{recipient}"))],
});
}
}
}
}
let itip_snapshots = itip_snapshot(&itip, access_token.emails.as_slice(), false)?; let itip_snapshots = itip_snapshot(&itip, access_token.emails.as_slice(), false)?;
if !itip_snapshots.sender_is_organizer_or_attendee(sender) { if !itip_snapshots.sender_is_organizer_or_attendee(sender) {
return Err(ItipIngestError::Message( return Err(ItipIngestError::Message(