fix(calendar): send invitation emails client-side via iMIP
Publish Docker Image / prepare (push) Successful in 7s
Publish Docker Image / build (linux/amd64, ubuntu-latest) (push) Failing after 7s
Publish Docker Image / build (linux/arm64, ubuntu-24.04-arm) (push) Canceled after 0s
Publish Docker Image / merge (push) Canceled after 0s
Publish Docker Image / prepare (push) Successful in 7s
Publish Docker Image / build (linux/amd64, ubuntu-latest) (push) Failing after 7s
Publish Docker Image / build (linux/arm64, ubuntu-24.04-arm) (push) Canceled after 0s
Publish Docker Image / merge (push) Canceled after 0s
This commit is contained in:
+26
-10
@@ -1,4 +1,4 @@
|
||||
import type { Email, Mailbox, MailboxRights, StateChange, AccountStates, Thread, Identity, EmailAddress, ContactCard, AddressBook, AddressBookRights, VacationResponse, Calendar, CalendarRights, CalendarEvent, CalendarEventFilter, CalendarTask, FileNode, FileNodeFilter, FileNodeRights, Principal, PushSubscription, EmailSubmission, ScheduledEmail, SendEmailResult, SharedAccount } from "./types";
|
||||
import type { Email, Mailbox, MailboxRights, StateChange, AccountStates, Thread, Identity, EmailAddress, ContactCard, AddressBook, AddressBookRights, VacationResponse, Calendar, CalendarRights, CalendarEvent, CalendarEventFilter, CalendarParticipant, CalendarTask, FileNode, FileNodeFilter, FileNodeRights, Principal, PushSubscription, EmailSubmission, ScheduledEmail, SendEmailResult, SharedAccount } from "./types";
|
||||
import type { SieveScript, SieveCapabilities } from "./sieve-types";
|
||||
import type { IJMAPClient } from "./client-interface";
|
||||
import { toWildcardQuery } from "./search-utils";
|
||||
@@ -3135,9 +3135,18 @@ export class JMAPClient implements IJMAPClient {
|
||||
throw new Error('No drafts mailbox found');
|
||||
}
|
||||
|
||||
// Find the organizer participant
|
||||
// Find the organizer participant. Stalwart may not echo `roles.owner`, so
|
||||
// also fall back to the event-level organizerCalendarAddress, and derive
|
||||
// participant emails from email / calendarAddress / sendTo.imip.
|
||||
const participantEmail = (p: CalendarParticipant): string =>
|
||||
p.email
|
||||
|| p.calendarAddress?.replace(/^mailto:/i, '')
|
||||
|| p.sendTo?.imip?.replace(/^mailto:/i, '')
|
||||
|| '';
|
||||
const organizerEntry = Object.values(event.participants).find(p => p.roles?.owner);
|
||||
const organizerEmail = organizerEntry?.email || organizerEntry?.sendTo?.imip?.replace('mailto:', '') || this.username;
|
||||
const organizerEmail = (organizerEntry ? participantEmail(organizerEntry) : '')
|
||||
|| event.organizerCalendarAddress?.replace(/^mailto:/i, '')
|
||||
|| this.username;
|
||||
const organizerName = organizerEntry?.name || '';
|
||||
|
||||
// Resolve identity
|
||||
@@ -3152,7 +3161,7 @@ export class JMAPClient implements IJMAPClient {
|
||||
}
|
||||
|
||||
// Collect attendee participants (non-organizer)
|
||||
const attendees = Object.values(event.participants).filter(p => !p.roles?.owner);
|
||||
const attendees = Object.values(event.participants).filter(p => !p.roles?.owner && participantEmail(p) !== organizerEmail);
|
||||
if (attendees.length === 0) return;
|
||||
|
||||
const now = new Date().toISOString().replace(/[-:]/g, '').replace(/\.\d{3}/, '');
|
||||
@@ -3212,7 +3221,7 @@ export class JMAPClient implements IJMAPClient {
|
||||
lines.push(`ORGANIZER${orgCn}:mailto:${organizerEmail}`);
|
||||
|
||||
for (const attendee of attendees) {
|
||||
const email = attendee.email || attendee.sendTo?.imip?.replace('mailto:', '');
|
||||
const email = participantEmail(attendee);
|
||||
if (!email) continue;
|
||||
const cn = attendee.name ? `;CN=${icsParamValue(attendee.name)}` : '';
|
||||
const partstat = attendee.participationStatus
|
||||
@@ -3228,7 +3237,7 @@ export class JMAPClient implements IJMAPClient {
|
||||
|
||||
const subject = `Invitation: ${event.title || 'Event'}`;
|
||||
const toAddresses = attendees
|
||||
.map(a => ({ name: a.name || undefined, email: a.email || a.sendTo?.imip?.replace('mailto:', '') || '' }))
|
||||
.map(a => ({ name: a.name || undefined, email: participantEmail(a) }))
|
||||
.filter(a => a.email);
|
||||
|
||||
if (toAddresses.length === 0) return;
|
||||
@@ -3313,8 +3322,15 @@ export class JMAPClient implements IJMAPClient {
|
||||
throw new Error('No drafts mailbox found');
|
||||
}
|
||||
|
||||
const participantEmail = (p: CalendarParticipant): string =>
|
||||
p.email
|
||||
|| p.calendarAddress?.replace(/^mailto:/i, '')
|
||||
|| p.sendTo?.imip?.replace(/^mailto:/i, '')
|
||||
|| '';
|
||||
const organizerEntry = Object.values(event.participants).find(p => p.roles?.owner);
|
||||
const organizerEmail = organizerEntry?.email || organizerEntry?.sendTo?.imip?.replace('mailto:', '') || this.username;
|
||||
const organizerEmail = (organizerEntry ? participantEmail(organizerEntry) : '')
|
||||
|| event.organizerCalendarAddress?.replace(/^mailto:/i, '')
|
||||
|| this.username;
|
||||
const organizerName = organizerEntry?.name || '';
|
||||
|
||||
const identityResponse = await this.request([
|
||||
@@ -3327,7 +3343,7 @@ export class JMAPClient implements IJMAPClient {
|
||||
identityId = match?.id || identities[0]?.id || this.accountId;
|
||||
}
|
||||
|
||||
const attendees = Object.values(event.participants).filter(p => !p.roles?.owner);
|
||||
const attendees = Object.values(event.participants).filter(p => !p.roles?.owner && participantEmail(p) !== organizerEmail);
|
||||
if (attendees.length === 0) return;
|
||||
|
||||
const now = new Date().toISOString().replace(/[-:]/g, '').replace(/\.\d{3}/, '');
|
||||
@@ -3370,7 +3386,7 @@ export class JMAPClient implements IJMAPClient {
|
||||
lines.push(`ORGANIZER${orgCn}:mailto:${organizerEmail}`);
|
||||
|
||||
for (const attendee of attendees) {
|
||||
const email = attendee.email || attendee.sendTo?.imip?.replace('mailto:', '');
|
||||
const email = participantEmail(attendee);
|
||||
if (!email) continue;
|
||||
const cn = attendee.name ? `;CN=${icsParamValue(attendee.name)}` : '';
|
||||
lines.push(`ATTENDEE${cn}:mailto:${email}`);
|
||||
@@ -3382,7 +3398,7 @@ export class JMAPClient implements IJMAPClient {
|
||||
|
||||
const subject = `Cancelled: ${event.title || 'Event'}`;
|
||||
const toAddresses = attendees
|
||||
.map(a => ({ name: a.name || undefined, email: a.email || a.sendTo?.imip?.replace('mailto:', '') || '' }))
|
||||
.map(a => ({ name: a.name || undefined, email: participantEmail(a) }))
|
||||
.filter(a => a.email);
|
||||
|
||||
if (toAddresses.length === 0) return;
|
||||
|
||||
Reference in New Issue
Block a user