fix: check a meeting reply's sender against the attendee who answers, not the organizer

This commit is contained in:
Linus Rath
2026-10-26 15:15:58 +01:00
parent e120ac6361
commit f0392a6705
32 changed files with 133 additions and 13 deletions
@@ -97,6 +97,7 @@ vi.mock('next-intl', () => ({
actor_note: 'Note: {comment}',
actor_unknown: 'Someone',
sender_mismatch_unverified_info: 'This invitation was sent from {sender}, while the organizer listed in the calendar data is {organizer}, and the message could not be verified.',
attendee_mismatch_unverified_info: 'This response was sent from {sender}, while the attendee answering in the calendar data is {attendee}, and the message could not be verified.',
action_failed: 'Could not complete that calendar action.',
proposal_applied: 'Proposed changes applied.',
proposed_changes: 'Proposed changes',
@@ -890,6 +891,32 @@ describe('CalendarInvitationBanner', () => {
expect(mocks.calendarState.updateEvent).not.toHaveBeenCalled();
});
it("checks a reply's sender against the attendee who answers, not the organizer", async () => {
const reply = {
uid: 'uid-14', title: 'Sync', start: '2026-03-27T09:00:00Z', duration: 'PT1H',
organizerCalendarAddress: 'mailto:user@example.com',
participants: {
a: { '@type': 'Participant', calendarAddress: 'mailto:user@example.com', roles: { owner: true, attendee: true }, participationStatus: 'accepted' },
b: { '@type': 'Participant', calendarAddress: 'mailto:alice@example.com', roles: { attendee: true }, participationStatus: 'accepted' },
},
};
mocks.clientMock.parseCalendarEvents.mockResolvedValue([reply]);
const replyFrom = (id: string, from: string) => makeEmail({
id, from: [{ email: from }],
attachments: [{ partId: '1', blobId: `blob-${id}`, size: 256, type: 'text/calendar; method=REPLY', name: 'reply.ics' }],
});
const { unmount } = render(<CalendarInvitationBanner email={replyFrom('email-14', 'alice@example.com')} />);
await waitFor(() => expect(screen.getByRole('heading', { name: 'Sync' })).toBeInTheDocument());
expect(screen.queryByText(/was sent from/)).toBeNull();
unmount();
render(<CalendarInvitationBanner email={replyFrom('email-15', 'mallory@evil.example')} />);
await waitFor(() => expect(screen.getByText(
'This response was sent from mallory@evil.example, while the attendee answering in the calendar data is alice@example.com, and the message could not be verified.',
)).toBeInTheDocument());
});
it('lets a proposal from an attendee Stalwart lists by calendar address be applied', async () => {
mocks.calendarState.updateEvent = vi.fn().mockResolvedValue(true);
// As 0.16 and 1.0 store and parse them: mailto: calendar addresses, no email.
@@ -155,6 +155,16 @@ function getTrustMessage(
sender: trustAssessment.senderEmail ?? '',
organizer: trustAssessment.organizerEmail ?? '',
});
case 'attendee_mismatch':
return t('attendee_mismatch_info', {
sender: trustAssessment.senderEmail ?? '',
attendee: trustAssessment.attendeeEmail ?? '',
});
case 'attendee_mismatch_unverified':
return t('attendee_mismatch_unverified_info', {
sender: trustAssessment.senderEmail ?? '',
attendee: trustAssessment.attendeeEmail ?? '',
});
default:
return null;
}
+22
View File
@@ -478,6 +478,28 @@ describe('getInvitationTrustAssessment', () => {
expect(result.reason).toBeNull();
});
// A reply comes from the attendee, and the organizer is the user reading
// it: comparing with the organizer warned on every genuine reply, and a
// forged one looked just the same.
it.each(['reply', 'counter', 'refresh'] as const)('compares the sender of a %s with the attendee who answers', (method) => {
const event: Partial<CalendarEvent> = {
participants: {
// As Stalwart parses it: the organizer is an attendee too, addresses as mailto:.
org: makeParticipant({ roles: { owner: true, attendee: true }, email: undefined, calendarAddress: 'mailto:me@example.com', participationStatus: 'accepted' }),
bob: makeParticipant({ roles: { attendee: true }, email: undefined, calendarAddress: 'mailto:Bob@example.net', participationStatus: 'accepted' }),
},
};
const aligned = { dkim: { result: 'pass', domain: 'example.net', selector: 's' } } as const;
const genuine = getInvitationTrustAssessment(event, makeEmail({ from: [{ email: 'bob@example.net' }], authenticationResults: aligned }), method);
expect(genuine.level).toBe('trusted');
const forged = getInvitationTrustAssessment(event, makeEmail({ from: [{ email: 'mallory@evil.example' }] }), method);
expect(forged).toMatchObject({
level: 'warning', reason: 'attendee_mismatch_unverified', senderEmail: 'mallory@evil.example', attendeeEmail: 'bob@example.net',
});
});
it('does not trust an SPF or DKIM pass for a domain other than the From domain', () => {
// MAIL FROM at the attacker's domain (SPF pass there), From and ORGANIZER
// at the victim's; the victim publishes no DMARC policy, so dmarc=none.
+18 -13
View File
@@ -21,9 +21,13 @@ export interface InvitationTrustAssessment {
| 'authentication_missing'
| 'sender_mismatch'
| 'sender_mismatch_unverified'
| 'attendee_mismatch'
| 'attendee_mismatch_unverified'
| null;
senderEmail: string | null;
organizerEmail: string | null;
/** The attendee who answers in a reply, counter or refresh. */
attendeeEmail: string | null;
}
export interface InvitationActorSummary {
@@ -399,17 +403,22 @@ export function getInvitationTrustAssessment(
): InvitationTrustAssessment {
const organizerEmail = getOrganizerEmail(event);
const senderEmail = getPrimaryAddressEmail(email?.from) || getPrimaryAddressEmail(email?.replyTo);
// A reply, counter or refresh is sent by the attendee who answers; the
// organizer is usually the user reading it.
const fromAttendee = method === 'reply' || method === 'counter' || method === 'refresh';
const attendeeEmail = fromAttendee ? getInvitationActorSummary(event, method)?.email ?? null : null;
const expectedEmail = fromAttendee ? attendeeEmail : organizerEmail;
const verifiedAuthentication = hasVerifiedAuthentication(email);
const authenticationFailure = hasAuthenticationFailure(email);
const senderMismatch = Boolean(senderEmail && organizerEmail && senderEmail !== organizerEmail);
const senderMismatch = Boolean(senderEmail && expectedEmail && senderEmail !== expectedEmail);
const expectsAuthenticatedTransport = method !== 'unknown';
const addresses = { senderEmail, organizerEmail, attendeeEmail };
if (senderMismatch && (authenticationFailure || !verifiedAuthentication)) {
return {
level: 'warning',
reason: 'sender_mismatch_unverified',
senderEmail,
organizerEmail,
reason: fromAttendee ? 'attendee_mismatch_unverified' : 'sender_mismatch_unverified',
...addresses,
};
}
@@ -417,17 +426,15 @@ export function getInvitationTrustAssessment(
return {
level: 'warning',
reason: 'authentication_failed',
senderEmail,
organizerEmail,
...addresses,
};
}
if (senderMismatch) {
return {
level: 'caution',
reason: 'sender_mismatch',
senderEmail,
organizerEmail,
reason: fromAttendee ? 'attendee_mismatch' : 'sender_mismatch',
...addresses,
};
}
@@ -435,16 +442,14 @@ export function getInvitationTrustAssessment(
return {
level: 'caution',
reason: 'authentication_missing',
senderEmail,
organizerEmail,
...addresses,
};
}
return {
level: 'trusted',
reason: null,
senderEmail,
organizerEmail,
...addresses,
};
}
+2
View File
@@ -567,6 +567,8 @@
"authentication_missing_info": "لا تتضمن هذه الدعوة مصادقة بريد موثّقة. تأكد من التفاصيل مع المنظّم إذا بدا أي شيء غير معتاد.",
"sender_mismatch_info": "أُرسلت هذه الدعوة من {sender}، بينما المنظّم المدرج في بيانات التقويم هو {organizer}.",
"sender_mismatch_unverified_info": "أُرسلت هذه الدعوة من {sender}، بينما المنظّم المدرج في بيانات التقويم هو {organizer}، ولم يتم التحقق من الرسالة.",
"attendee_mismatch_info": "أُرسل هذا الرد من {sender}، بينما الحاضر المجيب في بيانات التقويم هو {attendee}.",
"attendee_mismatch_unverified_info": "أُرسل هذا الرد من {sender}، بينما الحاضر المجيب في بيانات التقويم هو {attendee}، ولم يتم التحقق من الرسالة.",
"organizer_role": "أنت منظّم هذا الحدث",
"your_response": "ردّك: {status}",
"response_needed": "بحاجة إلى رد",
+2
View File
@@ -567,6 +567,8 @@
"authentication_missing_info": "Aquesta invitació no inclou autenticació de correu verificada. Confirmeu els detalls amb l'organitzador si alguna cosa sembla estranya.",
"sender_mismatch_info": "Aquesta invitació s'ha enviat des de {sender}, mentre que l'organitzador indicat a les dades del calendari és {organizer}.",
"sender_mismatch_unverified_info": "Aquesta invitació s'ha enviat des de {sender}, mentre que l'organitzador indicat a les dades del calendari és {organizer}, i no s'ha pogut verificar el missatge.",
"attendee_mismatch_info": "Aquesta resposta s'ha enviat des de {sender}, mentre que l'assistent que respon a les dades del calendari és {attendee}.",
"attendee_mismatch_unverified_info": "Aquesta resposta s'ha enviat des de {sender}, mentre que l'assistent que respon a les dades del calendari és {attendee}, i no s'ha pogut verificar el missatge.",
"organizer_role": "Vós organitzeu aquest esdeveniment",
"your_response": "La vostra resposta: {status}",
"response_needed": "Cal resposta",
+2
View File
@@ -542,6 +542,8 @@
"authentication_missing_info": "Tato pozvánka neobsahuje ověření zprávy. Pokud se vám cokoli zdá neobvyklé, potvrďte si podrobnosti s organizátorem.",
"sender_mismatch_info": "Tato pozvánka byla odeslána od {sender}, zatímco organizátor v datech kalendáře je {organizer}.",
"sender_mismatch_unverified_info": "Tato pozvánka byla odeslána od {sender}, zatímco organizátor v datech kalendáře je {organizer}, a zprávu se nepodařilo ověřit.",
"attendee_mismatch_info": "Tato odpověď byla odeslána od {sender}, zatímco odpovídající účastník v datech kalendáře je {attendee}.",
"attendee_mismatch_unverified_info": "Tato odpověď byla odeslána od {sender}, zatímco odpovídající účastník v datech kalendáře je {attendee}, a zprávu se nepodařilo ověřit.",
"organizer_role": "Jste organizátorem této události",
"your_response": "Vaše odpověď: {status}",
"response_needed": "Vyžaduje odpověď",
+2
View File
@@ -567,6 +567,8 @@
"authentication_missing_info": "Denne invitation inkluderer ikke bekræftet mailgodkendelse. Bekræft detaljerne med organisatoren, hvis noget ser usædvanligt ud.",
"sender_mismatch_info": "Denne invitation blev sendt fra {sender}, mens organisatoren i kalenderdataene er {organizer}.",
"sender_mismatch_unverified_info": "Denne invitation blev sendt fra {sender}, mens organisatoren i kalenderdataene er {organizer}, og beskeden kunne ikke bekræftes.",
"attendee_mismatch_info": "Dette svar blev sendt fra {sender}, mens deltageren, der svarer i kalenderdataene, er {attendee}.",
"attendee_mismatch_unverified_info": "Dette svar blev sendt fra {sender}, mens deltageren, der svarer i kalenderdataene, er {attendee}, og beskeden kunne ikke bekræftes.",
"organizer_role": "Du organiserer denne begivenhed",
"your_response": "Dit svar: {status}",
"response_needed": "Kræver svar",
+2
View File
@@ -540,6 +540,8 @@
"authentication_missing_info": "Diese Einladung enthält keine verifizierte Mail-Authentifizierung. Bestätigen Sie die Details mit dem Organisator, wenn etwas ungewöhnlich wirkt.",
"sender_mismatch_info": "Diese Einladung wurde von {sender} gesendet, während der in den Kalenderdaten angegebene Organisator {organizer} ist.",
"sender_mismatch_unverified_info": "Diese Einladung wurde von {sender} gesendet, während der in den Kalenderdaten angegebene Organisator {organizer} ist, und die Nachricht konnte nicht verifiziert werden.",
"attendee_mismatch_info": "Diese Antwort wurde von {sender} gesendet, während der antwortende Teilnehmer in den Kalenderdaten {attendee} ist.",
"attendee_mismatch_unverified_info": "Diese Antwort wurde von {sender} gesendet, während der antwortende Teilnehmer in den Kalenderdaten {attendee} ist, und die Nachricht konnte nicht verifiziert werden.",
"organizer_role": "Sie organisieren dieses Ereignis",
"your_response": "Ihre Antwort: {status}",
"response_needed": "Antwort ausstehend",
+2
View File
@@ -567,6 +567,8 @@
"authentication_missing_info": "This invitation does not include verified mail authentication. Confirm the details with the organizer if anything looks unusual.",
"sender_mismatch_info": "This invitation was sent from {sender}, while the organizer listed in the calendar data is {organizer}.",
"sender_mismatch_unverified_info": "This invitation was sent from {sender}, while the organizer listed in the calendar data is {organizer}, and the message could not be verified.",
"attendee_mismatch_info": "This response was sent from {sender}, while the attendee answering in the calendar data is {attendee}.",
"attendee_mismatch_unverified_info": "This response was sent from {sender}, while the attendee answering in the calendar data is {attendee}, and the message could not be verified.",
"organizer_role": "You organize this event",
"your_response": "Your response: {status}",
"response_needed": "Needs response",
+2
View File
@@ -540,6 +540,8 @@
"authentication_missing_info": "Esta invitación no incluye autenticación de correo verificada. Confirma los detalles con el organizador si algo parece inusual.",
"sender_mismatch_info": "Esta invitación se envió desde {sender}, mientras que el organizador indicado en los datos del calendario es {organizer}.",
"sender_mismatch_unverified_info": "Esta invitación se envió desde {sender}, mientras que el organizador indicado en los datos del calendario es {organizer}, y el mensaje no pudo verificarse.",
"attendee_mismatch_info": "Esta respuesta se envió desde {sender}, mientras que el asistente que responde en los datos del calendario es {attendee}.",
"attendee_mismatch_unverified_info": "Esta respuesta se envió desde {sender}, mientras que el asistente que responde en los datos del calendario es {attendee}, y el mensaje no pudo verificarse.",
"organizer_role": "Organizas este evento",
"your_response": "Tu respuesta: {status}",
"response_needed": "Pendiente de respuesta",
+2
View File
@@ -567,6 +567,8 @@
"authentication_missing_info": "Sellel kutsel puudub kinnitatud e-posti autentimine. Kui midagi tundub kahtlane, kontrolli üksikasju korraldajaga.",
"sender_mismatch_info": "See kutse saadeti aadressilt {sender}, kuid kalendriandmetes märgitud korraldaja on {organizer}.",
"sender_mismatch_unverified_info": "See kutse saadeti aadressilt {sender}, kuid kalendriandmetes märgitud korraldaja on {organizer}, ning kirja ei õnnestunud kinnitada.",
"attendee_mismatch_info": "See vastus saadeti aadressilt {sender}, kuid kalendriandmetes vastav osaleja on {attendee}.",
"attendee_mismatch_unverified_info": "See vastus saadeti aadressilt {sender}, kuid kalendriandmetes vastav osaleja on {attendee}, ning kirja ei õnnestunud kinnitada.",
"organizer_role": "Sina oled selle sündmuse korraldaja",
"your_response": "Sinu vastus: {status}",
"response_needed": "Vajab vastust",
+2
View File
@@ -567,6 +567,8 @@
"authentication_missing_info": "این دعوت‌نامه فاقد احراز هویت ایمیل است.",
"sender_mismatch_info": "این دعوت‌نامه از {sender} ارسال شده ولی برگزارکننده {organizer} است.",
"sender_mismatch_unverified_info": "از {sender} ارسال شده ولی برگزارکننده {organizer} است و پیام تأیید نشد.",
"attendee_mismatch_info": "این پاسخ از {sender} ارسال شده ولی شرکت‌کننده‌ی پاسخ‌دهنده {attendee} است.",
"attendee_mismatch_unverified_info": "این پاسخ از {sender} ارسال شده ولی شرکت‌کننده‌ی پاسخ‌دهنده {attendee} است و پیام تأیید نشد.",
"organizer_role": "شما برگزارکننده این رویداد هستید",
"your_response": "پاسخ شما: {status}",
"response_needed": "نیاز به پاسخ",
+2
View File
@@ -540,6 +540,8 @@
"authentication_missing_info": "Cette invitation ne comporte pas d'authentification de messagerie vérifiée. Vérifiez les détails avec l'organisateur si quelque chose semble inhabituel.",
"sender_mismatch_info": "Cette invitation a été envoyée depuis {sender}, alors que l'organisateur indiqué dans les données du calendrier est {organizer}.",
"sender_mismatch_unverified_info": "Cette invitation a été envoyée depuis {sender}, alors que l'organisateur indiqué dans les données du calendrier est {organizer}, et le message n'a pas pu être vérifié.",
"attendee_mismatch_info": "Cette réponse a été envoyée depuis {sender}, alors que le participant qui répond dans les données du calendrier est {attendee}.",
"attendee_mismatch_unverified_info": "Cette réponse a été envoyée depuis {sender}, alors que le participant qui répond dans les données du calendrier est {attendee}, et le message n'a pas pu être vérifié.",
"organizer_role": "Vous organisez cet événement",
"your_response": "Votre réponse : {status}",
"response_needed": "Réponse requise",
+2
View File
@@ -488,6 +488,8 @@
"authentication_missing_info": "הזמנה זו אינה כוללת אימות דואר מאומת. אשר את הפרטים עם המארגן אם משהו נראה חריג.",
"sender_mismatch_info": "הזמנה זו נשלחה מ-{sender}, בעוד שהמארגן הרשום בנתוני היומן הוא{organizer}.",
"sender_mismatch_unverified_info": "הזמנה זו נשלחה מ-{sender}, בעוד שהמארגן הרשום בנתוני היומן הוא{organizer}, ולא ניתן לאמת את ההודעה.",
"attendee_mismatch_info": "תגובה זו נשלחה מ-{sender}, בעוד שהמשתתף המשיב בנתוני היומן הוא {attendee}.",
"attendee_mismatch_unverified_info": "תגובה זו נשלחה מ-{sender}, בעוד שהמשתתף המשיב בנתוני היומן הוא {attendee}, ולא ניתן לאמת את ההודעה.",
"organizer_role": "אתה מארגן את האירוע הזה",
"your_response": "תגובתך:{status}",
"response_needed": "צריך תגובה",
+2
View File
@@ -567,6 +567,8 @@
"authentication_missing_info": "Ez a meghívó nem tartalmaz ellenőrzött e-mail hitelesítést. Ha bármi szokatlannak tűnik, erősítsd meg a részleteket a szervezővel.",
"sender_mismatch_info": "Ezt a meghívót {sender} küldte, míg a naptár adatokban szereplő szervező {organizer}.",
"sender_mismatch_unverified_info": "Ezt a meghívót {sender} küldte, míg a naptár adatokban szereplő szervező {organizer}, és az üzenet nem volt hitelesíthető.",
"attendee_mismatch_info": "Ezt a választ {sender} küldte, míg a naptár adatokban válaszoló résztvevő {attendee}.",
"attendee_mismatch_unverified_info": "Ezt a választ {sender} küldte, míg a naptár adatokban válaszoló résztvevő {attendee}, és az üzenet nem volt hitelesíthető.",
"organizer_role": "Te szervezed ezt az eseményt",
"your_response": "Válaszod: {status}",
"response_needed": "Válasz szükséges",
+2
View File
@@ -540,6 +540,8 @@
"authentication_missing_info": "Questo invito non include un'autenticazione email verificata. Conferma i dettagli con l'organizzatore se qualcosa sembra insolito.",
"sender_mismatch_info": "Questo invito è stato inviato da {sender}, mentre l'organizzatore indicato nei dati del calendario è {organizer}.",
"sender_mismatch_unverified_info": "Questo invito è stato inviato da {sender}, mentre l'organizzatore indicato nei dati del calendario è {organizer}, e il messaggio non ha potuto essere verificato.",
"attendee_mismatch_info": "Questa risposta è stata inviata da {sender}, mentre il partecipante che risponde nei dati del calendario è {attendee}.",
"attendee_mismatch_unverified_info": "Questa risposta è stata inviata da {sender}, mentre il partecipante che risponde nei dati del calendario è {attendee}, e il messaggio non ha potuto essere verificato.",
"organizer_role": "Sei l'organizzatore di questo evento",
"your_response": "La tua risposta: {status}",
"response_needed": "Risposta richiesta",
+2
View File
@@ -540,6 +540,8 @@
"authentication_missing_info": "この招待には検証済みのメール認証がありません。不審な点があれば主催者に詳細を確認してください。",
"sender_mismatch_info": "この招待は {sender} から送信されましたが、カレンダーデータ上の主催者は {organizer} です。",
"sender_mismatch_unverified_info": "この招待は {sender} から送信されましたが、カレンダーデータ上の主催者は {organizer} であり、メッセージも検証できませんでした。",
"attendee_mismatch_info": "この返信は {sender} から送信されましたが、カレンダーデータ上で返信している参加者は {attendee} です。",
"attendee_mismatch_unverified_info": "この返信は {sender} から送信されましたが、カレンダーデータ上で返信している参加者は {attendee} であり、メッセージも検証できませんでした。",
"organizer_role": "あなたがこのイベントの主催者です",
"your_response": "あなたの返答: {status}",
"response_needed": "返答が必要です",
+2
View File
@@ -542,6 +542,8 @@
"authentication_missing_info": "이 초대장에는 확인된 메일 인증 정보가 없어요. 이상한 점이 있다면 주최자에게 확인해 보세요.",
"sender_mismatch_info": "초대장은 {sender}님이 보냈지만, 캘린더에 등록된 주최자는 {organizer}님이에요.",
"sender_mismatch_unverified_info": "초대장은 {sender}님이 보냈지만 캘린더의 주최자는 {organizer}님이며, 메시지를 검증할 수 없어요.",
"attendee_mismatch_info": "이 응답은 {sender}님이 보냈지만 캘린더에서 응답한 참석자는 {attendee}님이에요.",
"attendee_mismatch_unverified_info": "이 응답은 {sender}님이 보냈지만 캘린더에서 응답한 참석자는 {attendee}님이며, 메시지를 검증할 수 없어요.",
"organizer_role": "내가 주최하는 일정이에요",
"your_response": "내 응답: {status}",
"response_needed": "응답이 필요해요",
+2
View File
@@ -542,6 +542,8 @@
"authentication_missing_info": "Šim uzaicinājumam nav apstiprinātas autentifikācijas.",
"sender_mismatch_info": "Uzaicinājumu nosūtīja {sender}, bet organizators ir {organizer}.",
"sender_mismatch_unverified_info": "Uzaicinājumu nosūtīja {sender}, bet norādītais organizators ir {organizer}, un ziņojumu nevarēja pārbaudīt.",
"attendee_mismatch_info": "Atbildi nosūtīja {sender}, bet atbildošais dalībnieks ir {attendee}.",
"attendee_mismatch_unverified_info": "Atbildi nosūtīja {sender}, bet atbildošais dalībnieks ir {attendee}, un ziņojumu nevarēja pārbaudīt.",
"organizer_role": "Jūs esat šī pasākuma organizators",
"your_response": "Jūsu atbilde: {status}",
"response_needed": "Nepieciešama atbilde",
+2
View File
@@ -567,6 +567,8 @@
"authentication_missing_info": "Энэ урилгад баталгаажуулсан имэйлийн баталгаажуулалт ороогүй болно. Ер бусын зүйл байвал зохион байгуулагчтай дэлгэрэнгүй мэдээллийг баталгаажуулна уу.",
"sender_mismatch_info": "Энэ урилгыг {sender}-с илгээсэн бол хуанлийн өгөгдөлд жагсаасан зохион байгуулагч нь {organizer} байна.",
"sender_mismatch_unverified_info": "Энэ урилгыг {sender}-с илгээсэн бөгөөд хуанлийн өгөгдөлд жагсаасан зохион байгуулагч нь {organizer} бөгөөд мессежийг баталгаажуулж чадсангүй.",
"attendee_mismatch_info": "Энэ хариуг {sender}-с илгээсэн бөгөөд хуанлийн өгөгдөлд хариулж буй оролцогч нь {attendee}.",
"attendee_mismatch_unverified_info": "Энэ хариуг {sender}-с илгээсэн бөгөөд хуанлийн өгөгдөлд хариулж буй оролцогч нь {attendee} бөгөөд мессежийг баталгаажуулж чадсангүй.",
"organizer_role": "Та энэ арга хэмжээг зохион байгуулна",
"your_response": "Таны хариулт: {status}",
"response_needed": "Хариулт хэрэгтэй",
+2
View File
@@ -567,6 +567,8 @@
"authentication_missing_info": "Denne invitasjonen har ingen bekreftet e-postautentisering. Avklar detaljene med arrangøren hvis noe ser uvanlig ut.",
"sender_mismatch_info": "Denne invitasjonen ble sendt fra {sender}, mens arrangøren som er oppført i kalenderdataene er {organizer}.",
"sender_mismatch_unverified_info": "Denne invitasjonen ble sendt fra {sender}, mens arrangøren som er oppført i kalenderdataene er {organizer}, og meldingen kunne ikke bekreftes.",
"attendee_mismatch_info": "Dette svaret ble sendt fra {sender}, mens deltakeren som svarer i kalenderdataene er {attendee}.",
"attendee_mismatch_unverified_info": "Dette svaret ble sendt fra {sender}, mens deltakeren som svarer i kalenderdataene er {attendee}, og meldingen kunne ikke bekreftes.",
"organizer_role": "Du er arrangør for denne hendelsen",
"your_response": "Ditt svar: {status}",
"response_needed": "Svar kreves",
+2
View File
@@ -540,6 +540,8 @@
"authentication_missing_info": "Deze uitnodiging bevat geen geverifieerde mailauthenticatie. Controleer de details bij de organisator als iets ongewoon lijkt.",
"sender_mismatch_info": "Deze uitnodiging is verzonden vanaf {sender}, terwijl de organisator in de kalendergegevens {organizer} is.",
"sender_mismatch_unverified_info": "Deze uitnodiging is verzonden vanaf {sender}, terwijl de organisator in de kalendergegevens {organizer} is, en het bericht kon niet worden geverifieerd.",
"attendee_mismatch_info": "Dit antwoord is verzonden vanaf {sender}, terwijl de antwoordende deelnemer in de kalendergegevens {attendee} is.",
"attendee_mismatch_unverified_info": "Dit antwoord is verzonden vanaf {sender}, terwijl de antwoordende deelnemer in de kalendergegevens {attendee} is, en het bericht kon niet worden geverifieerd.",
"organizer_role": "U organiseert dit evenement",
"your_response": "Uw reactie: {status}",
"response_needed": "Reactie vereist",
+2
View File
@@ -542,6 +542,8 @@
"authentication_missing_info": "To zaproszenie nie zawiera zweryfikowanego uwierzytelniania poczty. Potwierdź szczegóły z organizatorem, jeśli cokolwiek wygląda nietypowo.",
"sender_mismatch_info": "To zaproszenie zostało wysłane z {sender}, podczas gdy organizator w danych kalendarza to {organizer}.",
"sender_mismatch_unverified_info": "To zaproszenie zostało wysłane z {sender}, podczas gdy organizator w danych kalendarza to {organizer}, a wiadomość nie mogła zostać zweryfikowana.",
"attendee_mismatch_info": "Ta odpowiedź została wysłana z {sender}, podczas gdy odpowiadający uczestnik w danych kalendarza to {attendee}.",
"attendee_mismatch_unverified_info": "Ta odpowiedź została wysłana z {sender}, podczas gdy odpowiadający uczestnik w danych kalendarza to {attendee}, a wiadomość nie mogła zostać zweryfikowana.",
"organizer_role": "Jesteś organizatorem tego wydarzenia",
"your_response": "Twoja odpowiedź: {status}",
"response_needed": "Wymaga odpowiedzi",
+2
View File
@@ -540,6 +540,8 @@
"authentication_missing_info": "Este convite não inclui autenticação de email verificada. Confirme os detalhes com o organizador se algo parecer incomum.",
"sender_mismatch_info": "Este convite foi enviado por {sender}, enquanto o organizador indicado nos dados do calendário é {organizer}.",
"sender_mismatch_unverified_info": "Este convite foi enviado por {sender}, enquanto o organizador indicado nos dados do calendário é {organizer}, e a mensagem não pôde ser verificada.",
"attendee_mismatch_info": "Esta resposta foi enviada por {sender}, enquanto o participante que responde nos dados do calendário é {attendee}.",
"attendee_mismatch_unverified_info": "Esta resposta foi enviada por {sender}, enquanto o participante que responde nos dados do calendário é {attendee}, e a mensagem não pôde ser verificada.",
"organizer_role": "Você organiza este evento",
"your_response": "Sua resposta: {status}",
"response_needed": "Resposta necessária",
+2
View File
@@ -567,6 +567,8 @@
"authentication_missing_info": "Această invitație nu include autentificarea verificată a adresei de e-mail. Confirmați detaliile cu organizatorul dacă ceva pare neobișnuit.",
"sender_mismatch_info": "Această invitație a fost trimisă de la {sender}, în timp ce organizatorul menționat în datele din calendar este {organizer}.",
"sender_mismatch_unverified_info": "Această invitație a fost trimisă de la adresa {sender}, în timp ce organizatorul menționat în datele din calendar este {organizer}, iar mesajul nu a putut fi verificat.",
"attendee_mismatch_info": "Acest răspuns a fost trimis de la adresa {sender}, în timp ce participantul care răspunde în datele din calendar este {attendee}.",
"attendee_mismatch_unverified_info": "Acest răspuns a fost trimis de la adresa {sender}, în timp ce participantul care răspunde în datele din calendar este {attendee}, iar mesajul nu a putut fi verificat.",
"organizer_role": "Tu organizezi acest eveniment",
"your_response": "Răspunsul dvs.: {status}",
"response_needed": "Necesită răspuns",
+2
View File
@@ -542,6 +542,8 @@
"authentication_missing_info": "Это приглашение не содержит подтверждённой аутентификации почты. Если что-то кажется необычным, уточните детали у организатора.",
"sender_mismatch_info": "Это приглашение отправлено от {sender}, тогда как организатором в данных календаря указан {organizer}.",
"sender_mismatch_unverified_info": "Это приглашение отправлено от {sender}, тогда как организатором указан {organizer}, и сообщение не удалось проверить.",
"attendee_mismatch_info": "Этот ответ отправлен от {sender}, тогда как отвечающим участником указан {attendee}.",
"attendee_mismatch_unverified_info": "Этот ответ отправлен от {sender}, тогда как отвечающим участником указан {attendee}, и сообщение не удалось проверить.",
"organizer_role": "Вы являетесь организатором этого события",
"your_response": "Ваш ответ: {status}",
"response_needed": "Требуется ответ",
+2
View File
@@ -567,6 +567,8 @@
"authentication_missing_info": "Táto pozvánka neobsahuje overené overenie správ. Potvrdte podrobnosti s organizátorom.",
"sender_mismatch_info": "Táto pozvánka bola odoslaná od {sender}, zatiaľčo organizátor v dátach kalendára je {organizer}.",
"sender_mismatch_unverified_info": "Táto pozvánka bola odoslaná od {sender}, zatiaľčo organizátor v dátach kalendára je {organizer}, a správu sa nepodarilo overiť.",
"attendee_mismatch_info": "Táto odpoveď bola odoslaná od {sender}, zatiaľ čo odpovedajúci účastník v dátach kalendára je {attendee}.",
"attendee_mismatch_unverified_info": "Táto odpoveď bola odoslaná od {sender}, zatiaľ čo odpovedajúci účastník v dátach kalendára je {attendee}, a správu sa nepodarilo overiť.",
"organizer_role": "Ste organizátorom tejto udalosti",
"your_response": "Vaša odpoveď: {status}",
"response_needed": "Vyžaduje odpoveď",
+2
View File
@@ -542,6 +542,8 @@
"authentication_missing_info": "Bu davet doğrulanmış posta kimlik doğrulaması içermiyor. Bir şey olağandışı görünüyorsa ayrıntıları organizatörle teyit edin.",
"sender_mismatch_info": "Bu davet {sender} tarafından gönderildi; ancak takvim verisinde organizatör olarak {organizer} listeleniyor.",
"sender_mismatch_unverified_info": "Bu davet {sender} tarafından gönderildi; ancak takvim verisinde organizatör olarak {organizer} listeleniyor ve ileti doğrulanamadı.",
"attendee_mismatch_info": "Bu yanıt {sender} tarafından gönderildi; ancak takvim verisinde yanıtlayan katılımcı olarak {attendee} listeleniyor.",
"attendee_mismatch_unverified_info": "Bu yanıt {sender} tarafından gönderildi; ancak takvim verisinde yanıtlayan katılımcı olarak {attendee} listeleniyor ve ileti doğrulanamadı.",
"organizer_role": "Bu etkinliği siz düzenliyorsunuz",
"your_response": "Yanıtınız: {status}",
"response_needed": "Yanıt gerekiyor",
+2
View File
@@ -542,6 +542,8 @@
"authentication_missing_info": "Це запрошення не включає перевірену автентифікацію електронної пошти. Уточнюйте деталі в організатора, якщо щось виглядає незвичним.",
"sender_mismatch_info": "Це запрошення надіслано з {sender}, а організатор, зазначений у даних календаря, {organizer}.",
"sender_mismatch_unverified_info": "Це запрошення було надіслано з {sender}, а організатором, указаним у даних календаря, є {organizer}, і повідомлення не вдалося перевірити.",
"attendee_mismatch_info": "Цю відповідь було надіслано з {sender}, а учасником, який відповідає, у даних календаря є {attendee}.",
"attendee_mismatch_unverified_info": "Цю відповідь було надіслано з {sender}, а учасником, який відповідає, у даних календаря є {attendee}, і повідомлення не вдалося перевірити.",
"organizer_role": "Ви організовуєте цю подію",
"your_response": "Ваша відповідь: {status}",
"response_needed": "Потребує відповіді",
+2
View File
@@ -567,6 +567,8 @@
"authentication_missing_info": "這封邀請未包含已驗證的郵件身分驗證資訊。若有任何可疑之處,請向主辦人確認詳細資料。",
"sender_mismatch_info": "這封邀請是由 {sender} 傳送,但行事曆資料中列出的主辦人是 {organizer}。",
"sender_mismatch_unverified_info": "這封邀請是由 {sender} 傳送,但行事曆資料中列出的主辦人是 {organizer},而且無法驗證這封郵件。",
"attendee_mismatch_info": "這則回覆是由 {sender} 傳送,但行事曆資料中回覆的參與者是 {attendee}。",
"attendee_mismatch_unverified_info": "這則回覆是由 {sender} 傳送,但行事曆資料中回覆的參與者是 {attendee},而且無法驗證這封郵件。",
"organizer_role": "您是此活動的主辦人",
"your_response": "您的回覆:{status}",
"response_needed": "待回覆",
+2
View File
@@ -542,6 +542,8 @@
"authentication_missing_info": "此邀请不包括经过验证的邮件身份验证。如果有任何异常情况,请与组织者确认详细信息。",
"sender_mismatch_info": "此邀请是从 {sender} 发送的,而日历数据中列出的组织者是 {organizer}。",
"sender_mismatch_unverified_info": "此邀请是从 {sender} 发送的,而日历数据中列出的组织者是 {organizer},且无法验证该消息。",
"attendee_mismatch_info": "此回复是从 {sender} 发送的,而日历数据中作出回复的参与者是 {attendee}。",
"attendee_mismatch_unverified_info": "此回复是从 {sender} 发送的,而日历数据中作出回复的参与者是 {attendee},且无法验证该消息。",
"organizer_role": "您是此活动的组织者",
"your_response": "您的回复:{status}",
"response_needed": "待回复",