From 55631bc828183458dffc67decd353cf2bf052aca Mon Sep 17 00:00:00 2001 From: Maurus Decimus <11444311+mdecimus@users.noreply.github.com> Date: Fri, 4 Sep 2026 11:16:17 +0200 Subject: [PATCH] Fix iTIP: Detaching an occurrence that the recurrence rule already generates is sent as a `METHOD:REQUEST` carrying the `RECURRENCE-ID` instead of a `METHOD:ADD` --- CHANGELOG.md | 1 + crates/groupware/src/scheduling/organizer.rs | 2 +- tests/resources/itip/rfc5546_event_recurring.txt | 8 ++++---- 3 files changed, 6 insertions(+), 5 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index e92fdd1f0..be1050ca6 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -15,6 +15,7 @@ If you are upgrading from v0.16.x, replace the binary (or run `docker pull`). If - `CalendarEvent/set` requests that ask for scheduling messages are rejected with a `forbidden` error when the account cannot send them. - `Calendar/get` and `AddressBook/get` return every property when the `properties` argument is omitted or `null`. - IMAP: Every command in a pipelined `STATUS` or `FETCH` batch receives its tagged completion, instead of the first failing command dropping the responses for all commands queued behind it. +- iTIP: Detaching an occurrence that the recurrence rule already generates is sent as a `METHOD:REQUEST` carrying the `RECURRENCE-ID` instead of a `METHOD:ADD`. - LDAP: Active Directory servers that answer an unauthenticated bind (a non-empty DN with a zero-length password) with success no longer authenticate accounts without a password. - Network: Listeners bound to the unspecified IPv6 address (`[::]`) fall back to IPv4 when socket creation fails with `EPROTONOSUPPORT`. diff --git a/crates/groupware/src/scheduling/organizer.rs b/crates/groupware/src/scheduling/organizer.rs index 7aae19de0..265b17e0d 100644 --- a/crates/groupware/src/scheduling/organizer.rs +++ b/crates/groupware/src/scheduling/organizer.rs @@ -91,7 +91,7 @@ pub(crate) fn organizer_handle_update( let method = if matches!(instance.comp.status(), Some(ICalendarStatus::Cancelled)) { &ICalendarMethod::Cancel } else { - &ICalendarMethod::Add + &ICalendarMethod::Request }; changed_instances.extend(instance.attendees.iter().filter_map(|attendee| { diff --git a/tests/resources/itip/rfc5546_event_recurring.txt b/tests/resources/itip/rfc5546_event_recurring.txt index cf428ab5e..bb6953015 100644 --- a/tests/resources/itip/rfc5546_event_recurring.txt +++ b/tests/resources/itip/rfc5546_event_recurring.txt @@ -108,7 +108,7 @@ END:VCALENDAR > expect from: a@example.com to: b@example.com, c@example.com, d@example.com -summary: update ADD +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: "c@example.com", name: None, is_organizer: false }, ItipParticipant { email: "d@example.com", name: None, is_organizer: false }]) summary.description: Text("IETF-C&S Conference Call") summary.dtstart: Time(ItipTime { start: 865198800, tz_id: 32768 }) @@ -116,7 +116,7 @@ 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, rscale: None, skip: None }) summary.summary: Text("IETF Calendaring Working Group Meeting") BEGIN:VCALENDAR -METHOD:ADD +METHOD:REQUEST PRODID:-//Stalwart Labs LLC//Stalwart Server//EN VERSION:2.0 BEGIN:VEVENT @@ -349,7 +349,7 @@ END:VCALENDAR > expect from: a@example.com to: b@example.com, c@example.com, d@example.com -summary: update ADD +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: "c@example.com", name: None, is_organizer: false }, ItipParticipant { email: "d@example.com", name: None, is_organizer: false }]) summary.description: Text("IETF-C&S Conference Call") summary.dtstart: Time(ItipTime { start: 865198800, tz_id: 32768 }) @@ -357,7 +357,7 @@ 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, rscale: None, skip: None }) summary.summary: Text("IETF Calendaring Working Group Meeting") BEGIN:VCALENDAR -METHOD:ADD +METHOD:REQUEST PRODID:-//Stalwart Labs LLC//Stalwart Server//EN VERSION:2.0 BEGIN:VEVENT