feat: skip server-managed vacation script in Sieve script handling

This commit is contained in:
Linus Rath
2026-03-29 17:18:33 +02:00
parent 75b5d31414
commit 9d1302ef0a
2 changed files with 43 additions and 3 deletions
+34
View File
@@ -339,5 +339,39 @@ describe('filter-store', () => {
await useFilterStore.getState().fetchFilters(mockClient as unknown as IJMAPClient);
expect(useFilterStore.getState().rawScript).toBe(script);
});
it('should skip the server-managed vacation script', async () => {
const { generateScript } = await import('@/lib/sieve/generator');
const rules = [makeRule({ name: 'MyFilter' })];
const script = generateScript(rules);
const mockClient = {
getSieveCapabilities: () => null,
getSieveScripts: async () => [
{ id: 'vac-1', name: 'vacation', blobId: 'bv', isActive: true },
{ id: 's1', name: 'filters', blobId: 'b1', isActive: false },
],
getSieveScriptContent: async (blobId: string) => {
if (blobId === 'b1') return script;
return 'require "vacation"; vacation "I am away";';
},
};
await useFilterStore.getState().fetchFilters(mockClient as unknown as IJMAPClient);
// Should pick the 'filters' script, not the 'vacation' one
expect(useFilterStore.getState().activeScriptId).toBe('s1');
expect(useFilterStore.getState().rules[0].name).toBe('MyFilter');
});
it('should handle only vacation script present (no filter scripts)', async () => {
const mockClient = {
getSieveCapabilities: () => null,
getSieveScripts: async () => [
{ id: 'vac-1', name: 'vacation', blobId: 'bv', isActive: true },
],
};
await useFilterStore.getState().fetchFilters(mockClient as unknown as IJMAPClient);
expect(useFilterStore.getState().activeScriptId).toBeNull();
expect(useFilterStore.getState().rules).toEqual([]);
});
});
});
+9 -3
View File
@@ -52,8 +52,12 @@ export const useFilterStore = create<FilterStore>()((set, get) => ({
const capabilities = client.getSieveCapabilities();
set({ sieveCapabilities: capabilities });
const scripts = await client.getSieveScripts();
debug.log('Sieve scripts fetched:', scripts.length);
const allScripts = await client.getSieveScripts();
debug.log('Sieve scripts fetched:', allScripts.length);
// Skip the server-managed 'vacation' script (RFC 9661 §4) — it can only
// be modified via VacationResponse/set, not SieveScript/set.
const scripts = allScripts.filter(s => s.name !== 'vacation');
const activeScript = scripts.find(s => s.isActive) || scripts[0];
if (!activeScript) {
@@ -163,7 +167,9 @@ export const useFilterStore = create<FilterStore>()((set, get) => ({
// Always re-fetch scripts from the server to get the current state
// after Stalwart may have rewritten the active script.
const scripts = await client.getSieveScripts();
const allScripts = await client.getSieveScripts();
// Skip the server-managed 'vacation' script (RFC 9661 §4)
const scripts = allScripts.filter(s => s.name !== 'vacation');
const activeScript = scripts.find(s => s.isActive) || scripts[0];
let rules = previousRules;