mirror of
https://github.com/bulwarkmail/native.git
synced 2026-10-31 16:06:32 +00:00
fix: forward out-of-office mail to a unicode domain in its ascii form (bulwarkmail/webmail#1100)
This commit is contained in:
@@ -15,6 +15,8 @@ import { useLocaleStore } from '../../stores/locale-store';
|
||||
import { useSettingsStore } from '../../stores/settings-store';
|
||||
import { isValidEmail } from '../../lib/recipients';
|
||||
import { ownDomains } from '../../lib/sieve/vacation-audience';
|
||||
import { vacationForwardAddress } from '../../lib/sieve/vacation-forward';
|
||||
import { toUnicodeEmail } from '../../lib/idn';
|
||||
import type { VacationForward } from '../../lib/sieve/types';
|
||||
import { htmlToPlainText } from '../../lib/compose-html';
|
||||
import { stripDangerousTags, escapeHtml } from '../../lib/email-html';
|
||||
@@ -65,7 +67,8 @@ export function VacationSettings() {
|
||||
// Seed the editor once per load; RichTextEditor only reads initialHtml on mount.
|
||||
const [editorSeed, setEditorSeed] = useState(store.htmlBody || '');
|
||||
const [forwardEnabled, setForwardEnabled] = useState(store.forward?.enabled ?? false);
|
||||
const [forwardTo, setForwardTo] = useState(store.forward?.to ?? '');
|
||||
// Shown with its domain in Unicode, as it was typed.
|
||||
const [forwardTo, setForwardTo] = useState(toUnicodeEmail(store.forward?.to ?? ''));
|
||||
const [forwardKeep, setForwardKeep] = useState(store.forward?.keepCopy ?? false);
|
||||
const [audienceOnly, setAudienceOnly] = useState<AudienceChoice>(store.audience?.only ?? 'all');
|
||||
// "Internal" are the account's own domains, as its identities give them.
|
||||
@@ -92,7 +95,7 @@ export function VacationSettings() {
|
||||
setHtmlBody(store.htmlBody || '');
|
||||
setEditorSeed(store.htmlBody || '');
|
||||
setForwardEnabled(store.forward?.enabled ?? false);
|
||||
setForwardTo(store.forward?.to ?? '');
|
||||
setForwardTo(toUnicodeEmail(store.forward?.to ?? ''));
|
||||
setForwardKeep(store.forward?.keepCopy ?? false);
|
||||
setAudienceOnly(store.audience?.only ?? 'all');
|
||||
}, [
|
||||
@@ -115,7 +118,8 @@ export function VacationSettings() {
|
||||
|
||||
// Forwarding as a save stores it: an address that is off is kept for next
|
||||
// time if it is usable, and dropped otherwise.
|
||||
const forwardAddress = forwardTo.trim();
|
||||
// Checked and stored with a Unicode domain in ASCII form (webmail #1100).
|
||||
const forwardAddress = vacationForwardAddress(forwardTo);
|
||||
const forwardInvalid = store.forwardAvailable && forwardEnabled && !isValidEmail(forwardAddress);
|
||||
const forwardSettings: VacationForwardSettings | null | undefined = !store.forwardAvailable
|
||||
? undefined
|
||||
|
||||
@@ -0,0 +1,29 @@
|
||||
import { describe, it, expect } from 'vitest';
|
||||
import fs from 'node:fs';
|
||||
import path from 'node:path';
|
||||
import { generateScript } from '../generator';
|
||||
import { isValidVacationForward, vacationForwardAddress } from '../vacation-forward';
|
||||
|
||||
// A forward address with a Unicode domain went into the Sieve redirect as
|
||||
// typed; the server needs the ASCII (xn--) form (bulwarkmail/webmail#1100).
|
||||
|
||||
describe('vacation forwarding to a Unicode domain', () => {
|
||||
it('writes the redirect with the domain in ASCII form', () => {
|
||||
const to = vacationForwardAddress(' Ich@bücher.de ');
|
||||
expect(to).toBe('Ich@xn--bcher-kva.de');
|
||||
const forward = { enabled: true, to, keepCopy: false };
|
||||
expect(isValidVacationForward(forward)).toBe(true);
|
||||
const script = generateScript([], undefined, { includeVacation: true, vacationForward: forward });
|
||||
expect(script).toContain('redirect "Ich@xn--bcher-kva.de";');
|
||||
expect(script).not.toContain('bücher');
|
||||
// ASCII addresses stay as they are.
|
||||
expect(vacationForwardAddress('a@example.org')).toBe('a@example.org');
|
||||
});
|
||||
|
||||
// No RN renderer in the test env: check the settings page's wiring.
|
||||
it('is what the out-of-office page checks and saves', () => {
|
||||
const page = fs.readFileSync(path.resolve(__dirname, '../../../components/settings/VacationSettings.tsx'), 'utf8');
|
||||
expect(page).toMatch(/const forwardAddress = vacationForwardAddress\(forwardTo\);/);
|
||||
expect(page).toMatch(/\{ enabled: forwardEnabled, to: forwardAddress, keepCopy: forwardKeep \}/);
|
||||
});
|
||||
});
|
||||
@@ -1,6 +1,7 @@
|
||||
import type { VacationForward } from './types';
|
||||
import { isValidEmail } from '../recipients';
|
||||
import { isPeriodBoundary } from './period';
|
||||
import { toAsciiEmail } from '../idn';
|
||||
|
||||
/**
|
||||
* Forwarding from the vacation card (see VacationForward). The generator
|
||||
@@ -15,6 +16,15 @@ export const VACATION_FORWARD_MARKER = '# Vacation forwarding';
|
||||
*/
|
||||
export const VACATION_FORWARD_MARKER_RE = /^[ \t]*# Vacation forwarding[ \t]*$/m;
|
||||
|
||||
/**
|
||||
* The forwarding address as typed, the way the Sieve redirect needs it: a
|
||||
* Unicode domain in its ASCII (`xn--`) form, which the server can deliver
|
||||
* to (webmail #1100).
|
||||
*/
|
||||
export function vacationForwardAddress(typed: string): string {
|
||||
return toAsciiEmail(typed.trim());
|
||||
}
|
||||
|
||||
/** Whether `value` is forwarding Bulwark can write back as it reads. */
|
||||
export function isValidVacationForward(value: unknown): value is VacationForward {
|
||||
if (!value || typeof value !== 'object') return false;
|
||||
|
||||
Reference in New Issue
Block a user