From 76ade5eac0e80f6a540673645b3131e0b3c5d20c Mon Sep 17 00:00:00 2001 From: the-djmaze <> Date: Tue, 24 Sep 2024 01:37:04 +0200 Subject: [PATCH] Get most valid Sieve scripts parsed properly --- dev/Sieve/Commands/Tests.js | 15 +++-- dev/Sieve/Extensions/rfc5173.js | 5 +- dev/Sieve/Extensions/rfc5183.js | 5 +- dev/Sieve/Extensions/rfc5229.js | 5 +- dev/Sieve/Extensions/rfc5232.js | 5 +- dev/Sieve/Extensions/rfc5235.js | 14 ++-- dev/Sieve/Extensions/rfc5260.js | 10 +-- dev/Sieve/Extensions/rfc5435.js | 5 +- dev/Sieve/Extensions/rfc5490.js | 10 +-- dev/Sieve/Grammar.js | 64 ++++++++++++++++--- dev/Sieve/Parser.js | 16 +++-- dev/Sieve/README.md | 1 + dev/Sieve/Utils.js | 34 +++++++++- .../app/templates/Views/Sieve/BodyTest.html | 5 +- .../Views/Sieve/CurrentDateTest.html | 5 +- .../app/templates/Views/Sieve/DateTest.html | 5 +- .../Views/Sieve/EnvironmentTest.html | 5 +- .../templates/Views/Sieve/HasFlagTest.html | 4 +- .../app/templates/Views/Sieve/HeaderTest.html | 5 +- .../templates/Views/Sieve/MetadataTest.html | 5 +- .../Sieve/NotifyMethodCapabilityTest.html | 5 +- .../Views/Sieve/ServerMetadataTest.html | 5 +- .../templates/Views/Sieve/SpamTestTest.html | 4 +- .../app/templates/Views/Sieve/StringTest.html | 5 +- .../templates/Views/Sieve/VirusTestTest.html | 4 +- 25 files changed, 175 insertions(+), 71 deletions(-) diff --git a/dev/Sieve/Commands/Tests.js b/dev/Sieve/Commands/Tests.js index e5eb4a341..093b746a2 100644 --- a/dev/Sieve/Commands/Tests.js +++ b/dev/Sieve/Commands/Tests.js @@ -68,9 +68,10 @@ export class AddressTest extends TestCommand } return result + (this.last ? ' :last' : (this.index.value ? ' :index ' + this.index : '')) - + (this.comparator ? ' :comparator ' + this.comparator : '') + + (this._comparator ? ' :comparator ' + this._comparator : '') + ' ' + this.address_part - + ' ' + this.match_type + + (this._match_type ? ' ' + this._match_type : '') + + (this.relational_match ? ' ' + this.relational_match : '') + ' ' + this.header_list + ' ' + this.key_list; } @@ -147,9 +148,10 @@ export class EnvelopeTest extends TestCommand toString() { return 'envelope' - + (this.comparator ? ' :comparator ' + this.comparator : '') + + (this._comparator ? ' :comparator ' + this._comparator : '') + ' ' + this.address_part - + ' ' + this.match_type + + (this._match_type ? ' ' + this._match_type : '') + + (this.relational_match ? ' ' + this.relational_match : '') + ' ' + this.envelope_part + ' ' + this.key_list; } @@ -279,8 +281,9 @@ export class HeaderTest extends TestCommand } return result + (this.last ? ' :last' : (this.index.value ? ' :index ' + this.index : '')) - + (this.comparator ? ' :comparator ' + this.comparator : '') - + ' ' + this.match_type + + (this._comparator ? ' :comparator ' + this._comparator : '') + + (this._match_type ? ' ' + this._match_type : '') + + (this.relational_match ? ' ' + this.relational_match : '') + ' ' + this.header_names + ' ' + this.key_list; } diff --git a/dev/Sieve/Extensions/rfc5173.js b/dev/Sieve/Extensions/rfc5173.js index d538735a4..d72b97f89 100644 --- a/dev/Sieve/Extensions/rfc5173.js +++ b/dev/Sieve/Extensions/rfc5173.js @@ -22,8 +22,9 @@ export class BodyTest extends TestCommand toString() { return 'body' - + (this.comparator ? ' :comparator ' + this.comparator : '') - + ' ' + this.match_type + + (this._comparator ? ' :comparator ' + this._comparator : '') + + (this._match_type ? ' ' + this._match_type : '') + + (this.relational_match ? ' ' + this.relational_match : '') + ' ' + this.body_transform + ' ' + this.key_list; } diff --git a/dev/Sieve/Extensions/rfc5183.js b/dev/Sieve/Extensions/rfc5183.js index a23e833df..65e51961b 100644 --- a/dev/Sieve/Extensions/rfc5183.js +++ b/dev/Sieve/Extensions/rfc5183.js @@ -25,8 +25,9 @@ export class EnvironmentTest extends TestCommand toString() { return 'environment' - + (this.comparator ? ' :comparator ' + this.comparator : '') - + ' ' + this.match_type + + (this._comparator ? ' :comparator ' + this._comparator : '') + + (this._match_type ? ' ' + this._match_type : '') + + (this.relational_match ? ' ' + this.relational_match : '') + ' ' + this._name + ' ' + this.key_list; } diff --git a/dev/Sieve/Extensions/rfc5229.js b/dev/Sieve/Extensions/rfc5229.js index 7bd3c3aeb..794260185 100644 --- a/dev/Sieve/Extensions/rfc5229.js +++ b/dev/Sieve/Extensions/rfc5229.js @@ -57,8 +57,9 @@ export class StringTest extends TestCommand toString() { return 'string' - + ' ' + this.match_type - + (this.comparator ? ' :comparator ' + this.comparator : '') + + (this._match_type ? ' ' + this._match_type : '') + + (this.relational_match ? ' ' + this.relational_match : '') + + (this._comparator ? ' :comparator ' + this._comparator : '') + ' ' + this.source + ' ' + this.key_list; } diff --git a/dev/Sieve/Extensions/rfc5232.js b/dev/Sieve/Extensions/rfc5232.js index 0577cde1d..ea56261f5 100644 --- a/dev/Sieve/Extensions/rfc5232.js +++ b/dev/Sieve/Extensions/rfc5232.js @@ -79,8 +79,9 @@ export class HasFlagTest extends TestCommand toString() { return 'hasflag' - + ' ' + this.match_type - + (this.comparator ? ' :comparator ' + this.comparator : '') + + (this._match_type ? ' ' + this._match_type : '') + + (this.relational_match ? ' ' + this.relational_match : '') + + (this._comparator ? ' :comparator ' + this._comparator : '') + ' ' + this.variable_list + ' ' + this.list_of_flags; } diff --git a/dev/Sieve/Extensions/rfc5235.js b/dev/Sieve/Extensions/rfc5235.js index 1c8bc46b2..ac1e8406e 100644 --- a/dev/Sieve/Extensions/rfc5235.js +++ b/dev/Sieve/Extensions/rfc5235.js @@ -17,7 +17,7 @@ export class SpamTestTest extends TestCommand } // get require() { return this.percent ? 'spamtestplus' : 'spamtest'; } - get require() { return /:value|:count/.test(this.match_type) ? ['spamtestplus','relational'] : 'spamtestplus'; } + get require() { return /:value|:count/.test(this._match_type) ? ['spamtestplus','relational'] : 'spamtestplus'; } get value() { return this._value.value; } set value(v) { this._value.value = v; } @@ -26,8 +26,9 @@ export class SpamTestTest extends TestCommand { return 'spamtest' + (this.percent ? ' :percent' : '') - + (this.comparator ? ' :comparator ' + this.comparator : '') - + ' ' + this.match_type + + (this._comparator ? ' :comparator ' + this._comparator : '') + + (this._match_type ? ' ' + this._match_type : '') + + (this.relational_match ? ' ' + this.relational_match : '') + ' ' + this._value; } @@ -51,7 +52,7 @@ export class VirusTestTest extends TestCommand this._value = new GrammarQuotedString; // 1 - 5 } - get require() { return /:value/.test(this.match_type) ? ['virustest','relational'] : 'virustest'; } + get require() { return ':value' == this._match_type ? ['virustest','relational'] : 'virustest'; } get value() { return this._value.value; } set value(v) { this._value.value = v; } @@ -59,8 +60,9 @@ export class VirusTestTest extends TestCommand toString() { return 'virustest' - + (this.comparator ? ' :comparator ' + this.comparator : '') - + ' ' + this.match_type + + (this._comparator ? ' :comparator ' + this._comparator : '') + + (this._match_type ? ' ' + this._match_type : '') + + (this.relational_match ? ' ' + this.relational_match : '') + ' ' + this._value; } diff --git a/dev/Sieve/Extensions/rfc5260.js b/dev/Sieve/Extensions/rfc5260.js index d888b72ae..2745a2087 100644 --- a/dev/Sieve/Extensions/rfc5260.js +++ b/dev/Sieve/Extensions/rfc5260.js @@ -41,8 +41,9 @@ export class DateTest extends TestCommand return 'date' + (this.last ? ' :last' : (this.index.value ? ' :index ' + this.index : '')) + (this.originalzone ? ' :originalzone' : (this._zone.length ? ' :zone ' + this._zone : '')) - + (this.comparator ? ' :comparator ' + this.comparator : '') - + ' ' + this.match_type + + (this._comparator ? ' :comparator ' + this._comparator : '') + + (this._match_type ? ' ' + this._match_type : '') + + (this.relational_match ? ' ' + this.relational_match : '') + ' ' + this._header_name + ' ' + this._date_part + ' ' + this.key_list; @@ -89,8 +90,9 @@ export class CurrentDateTest extends TestCommand { return 'currentdate' + (this._zone.length ? ' :zone ' + this._zone : '') - + (this.comparator ? ' :comparator ' + this.comparator : '') - + ' ' + this.match_type + + (this._comparator ? ' :comparator ' + this._comparator : '') + + (this._match_type ? ' ' + this._match_type : '') + + (this.relational_match ? ' ' + this.relational_match : '') + ' ' + this._date_part + ' ' + this.key_list; } diff --git a/dev/Sieve/Extensions/rfc5435.js b/dev/Sieve/Extensions/rfc5435.js index 9bee9fdb6..b6f040096 100644 --- a/dev/Sieve/Extensions/rfc5435.js +++ b/dev/Sieve/Extensions/rfc5435.js @@ -116,8 +116,9 @@ export class NotifyMethodCapabilityTest extends TestCommand toString() { return 'valid_notify_method ' - + (this.comparator ? ' :comparator ' + this.comparator : '') - + (this.match_type ? ' ' + this.match_type : '') + + (this._comparator ? ' :comparator ' + this._comparator : '') + + (this._match_type ? ' ' + this._match_type : '') + + (this.relational_match ? ' ' + this.relational_match : '') + this._notification_uri + this._notification_capability + this.key_list; diff --git a/dev/Sieve/Extensions/rfc5490.js b/dev/Sieve/Extensions/rfc5490.js index 036600763..94f7605b7 100644 --- a/dev/Sieve/Extensions/rfc5490.js +++ b/dev/Sieve/Extensions/rfc5490.js @@ -58,8 +58,9 @@ export class MetadataTest extends TestCommand toString() { return 'metadata ' - + ' ' + this.match_type - + (this.comparator ? ' :comparator ' + this.comparator : '') + + ' ' + this._match_type + + (this.relational_match ? ' ' + this.relational_match : '') + + (this._comparator ? ' :comparator ' + this._comparator : '') + ' ' + this._mailbox + ' ' + this._annotation_name + ' ' + this.key_list; @@ -124,8 +125,9 @@ export class ServerMetadataTest extends TestCommand toString() { return 'servermetadata ' - + ' ' + this.match_type - + (this.comparator ? ' :comparator ' + this.comparator : '') + + ' ' + this._match_type + + (this.relational_match ? ' ' + this.relational_match : '') + + (this._comparator ? ' :comparator ' + this._comparator : '') + ' ' + this._annotation_name + ' ' + this.key_list; } diff --git a/dev/Sieve/Grammar.js b/dev/Sieve/Grammar.js index 60f39cabd..d83df718b 100644 --- a/dev/Sieve/Grammar.js +++ b/dev/Sieve/Grammar.js @@ -9,7 +9,7 @@ import { MULTILINE_DOTSTART } from 'Sieve/RegEx'; -import { arrayToString, getMatchTypes } from 'Sieve/Utils'; +import { arrayToString, getMatchTypes, getComparators, koObserve } from 'Sieve/Utils'; /** * abstract @@ -151,19 +151,65 @@ export /*abstract*/ class TestCommand extends GrammarCommand */ super(identifier); // Almost every test has a comparator and match_type, so define them here - this.comparator = ''; - this.match_type = ':is'; + this._comparator = ''; + this._match_type = ''; + this.relational_match = ''; // GrammarQuotedString DQUOTE ( "gt" / "ge" / "lt" / "le" / "eq" / "ne" ) DQUOTE + koObserve(this, 'match_type'); + } + + get require() { return /:value|:count/.test(this._match_type) ? 'relational' : ''; } + + get match_type() + { + return this._match_type; + } + set match_type(value) + { + // default? + if (':is' == value) { + value = ''; + } + if (value.length && !getMatchTypes(0).includes(value)) { + throw 'Unsupported match-type ' + value; + } + if (':list' == value) { + this._comparator = ''; + } + if (':count' != value && ':value' != value) { + this.relational_match = ''; + } + this._match_type = value; + } + + get comparator() + { + return this._comparator; + } + set comparator(value) + { + if (!(value instanceof GrammarQuotedString)) { + value = new GrammarQuotedString(value); + } + // default? + if (value.length && 'i;ascii-casemap' != value.value) { + if (':list' == this._match_type) { + throw 'Comparator not allowed when using :list'; + } + if (!getComparators().includes(value.value)) { + throw 'Unsupported comparator ' + value; + } + this._comparator = value; + } else { + this._comparator = ''; + } } toString() { - // https://datatracker.ietf.org/doc/html/rfc6134#section-2.3 - if (!getMatchTypes().includes(this.match_type)) { - throw 'Unsupported match-type ' + this.match_type; - } return (this.identifier - + (this.comparator ? ' :comparator ' + this.comparator : '') - + (this.match_type ? ' ' + this.match_type : '') + + (this._comparator ? ' :comparator ' + this._comparator : '') + + (this._match_type ? ' ' + this._match_type : '') + + (this.relational_match ? ' ' + this.relational_match : '') + ' ' + arrayToString(this.arguments, ' ')).trim(); } } diff --git a/dev/Sieve/Parser.js b/dev/Sieve/Parser.js index 8ef1dacc3..8515d28b6 100644 --- a/dev/Sieve/Parser.js +++ b/dev/Sieve/Parser.js @@ -119,18 +119,20 @@ export const parseScript = (script, name = 'script.sieve') => { let prev_arg = args[args.length-1]; if (getMatchTypes(0).includes(arg)) { command.match_type = arg; - } else if (':value' === prev_arg || ':count' === prev_arg) { - // Sieve relational [RFC5231] match types - /^(gt|ge|lt|le|eq|ne)$/.test(arg.value) || error('Invalid relational match-type ' + arg); - command.match_type = prev_arg + ' ' + arg; + } else if (getMatchTypes(0).includes(prev_arg)) { --args.length; -// requires.push('relational'); + if (':value' === prev_arg || ':count' === prev_arg) { + // Sieve relational [RFC5231] match types + /^"(gt|ge|lt|le|eq|ne)"$/.test(arg) || error('Invalid relational match-type ' + arg); + command.relational_match = arg; +// requires.push('relational'); + return; + } } else if (':comparator' === prev_arg) { command.comparator = arg; --args.length; - } else { - args.push(arg); } + args.push(arg); }, pushArgs = () => { if (args.length) { diff --git a/dev/Sieve/README.md b/dev/Sieve/README.md index 2f802159b..9b3c44806 100644 --- a/dev/Sieve/README.md +++ b/dev/Sieve/README.md @@ -4,6 +4,7 @@ https://www.iana.org/assignments/sieve-extensions/sieve-extensions.xhtml - [ ] RFC3461 envelope-dsn / redirect-dsn - [x] RFC3894 copy - [ ] RFC4790 comparator-* +- [ ] RFC5051 i;unicode-casemap - [x] RFC5173 body - [x] RFC5183 environment - [x] RFC5228 encoded-character / envelope / fileinto diff --git a/dev/Sieve/Utils.js b/dev/Sieve/Utils.js index 266857aec..c671af8ae 100644 --- a/dev/Sieve/Utils.js +++ b/dev/Sieve/Utils.js @@ -58,13 +58,45 @@ export const serverErrorDesc(text); }, - getComparators = () => ['i;ascii-casemap'], + koObserve = (obj, prop) => { +// Object.getOwnPropertyNames(obj).forEach(prop => { +// prop = prop.replace(/^_/, ''); + obj['ko_' + prop] = ko.computed({ + read: () => obj[prop], + write: (v) => obj[prop] = v, + pure:true + }); +// }); +// Object.preventExtensions(obj); + }, + + getComparators = (validOnly = 0) => { + let result = [ + // Default + 'i;ascii-casemap', + ]; + if (capa.includes('relational') || !validOnly) { + result.push('i;octet'); + } + if (capa.includes('comparator-i;ascii-numeric') || !validOnly) { + result.push('i;ascii-numeric'); + } + if (capa.includes('comparator-i;unicode-casemap') || !validOnly) { + result.push('i;unicode-casemap'); + } + return result; + }, getMatchTypes = (validOnly = 1) => { let result = [':is',':contains',':matches']; // https://datatracker.ietf.org/doc/html/rfc6134#section-2.3 + // Only available for tests with a key_list property if (capa.includes('extlists') || !validOnly) { result.push(':list'); } + if (capa.includes('relational') || !validOnly) { + result.push(':value'); + result.push(':count'); + } return result; }; diff --git a/snappymail/v/0.0.0/app/templates/Views/Sieve/BodyTest.html b/snappymail/v/0.0.0/app/templates/Views/Sieve/BodyTest.html index 47a8fe1c9..8ef90f372 100644 --- a/snappymail/v/0.0.0/app/templates/Views/Sieve/BodyTest.html +++ b/snappymail/v/0.0.0/app/templates/Views/Sieve/BodyTest.html @@ -1,8 +1,9 @@ - + + body_transform = ''; // :raw, :content , :text diff --git a/snappymail/v/0.0.0/app/templates/Views/Sieve/CurrentDateTest.html b/snappymail/v/0.0.0/app/templates/Views/Sieve/CurrentDateTest.html index 0aa49db22..10660f9ee 100644 --- a/snappymail/v/0.0.0/app/templates/Views/Sieve/CurrentDateTest.html +++ b/snappymail/v/0.0.0/app/templates/Views/Sieve/CurrentDateTest.html @@ -1,9 +1,10 @@ currentdate - + + diff --git a/snappymail/v/0.0.0/app/templates/Views/Sieve/DateTest.html b/snappymail/v/0.0.0/app/templates/Views/Sieve/DateTest.html index d626484c0..2afd208d2 100644 --- a/snappymail/v/0.0.0/app/templates/Views/Sieve/DateTest.html +++ b/snappymail/v/0.0.0/app/templates/Views/Sieve/DateTest.html @@ -1,9 +1,10 @@ date - + + diff --git a/snappymail/v/0.0.0/app/templates/Views/Sieve/EnvironmentTest.html b/snappymail/v/0.0.0/app/templates/Views/Sieve/EnvironmentTest.html index 97a721abe..68a26f8bc 100644 --- a/snappymail/v/0.0.0/app/templates/Views/Sieve/EnvironmentTest.html +++ b/snappymail/v/0.0.0/app/templates/Views/Sieve/EnvironmentTest.html @@ -1,9 +1,10 @@ environment - + + diff --git a/snappymail/v/0.0.0/app/templates/Views/Sieve/HasFlagTest.html b/snappymail/v/0.0.0/app/templates/Views/Sieve/HasFlagTest.html index 0f1c02bb3..8e89073fc 100644 --- a/snappymail/v/0.0.0/app/templates/Views/Sieve/HasFlagTest.html +++ b/snappymail/v/0.0.0/app/templates/Views/Sieve/HasFlagTest.html @@ -1,7 +1,7 @@ hasflag - + diff --git a/snappymail/v/0.0.0/app/templates/Views/Sieve/HeaderTest.html b/snappymail/v/0.0.0/app/templates/Views/Sieve/HeaderTest.html index d8dddbe7f..9462e093b 100644 --- a/snappymail/v/0.0.0/app/templates/Views/Sieve/HeaderTest.html +++ b/snappymail/v/0.0.0/app/templates/Views/Sieve/HeaderTest.html @@ -1,10 +1,11 @@
- + +
metadata - + + diff --git a/snappymail/v/0.0.0/app/templates/Views/Sieve/NotifyMethodCapabilityTest.html b/snappymail/v/0.0.0/app/templates/Views/Sieve/NotifyMethodCapabilityTest.html index d85dec557..956106fce 100644 --- a/snappymail/v/0.0.0/app/templates/Views/Sieve/NotifyMethodCapabilityTest.html +++ b/snappymail/v/0.0.0/app/templates/Views/Sieve/NotifyMethodCapabilityTest.html @@ -1,9 +1,10 @@ notifymethodcapability - + + diff --git a/snappymail/v/0.0.0/app/templates/Views/Sieve/ServerMetadataTest.html b/snappymail/v/0.0.0/app/templates/Views/Sieve/ServerMetadataTest.html index f1ab26a8a..ac817870f 100644 --- a/snappymail/v/0.0.0/app/templates/Views/Sieve/ServerMetadataTest.html +++ b/snappymail/v/0.0.0/app/templates/Views/Sieve/ServerMetadataTest.html @@ -1,9 +1,10 @@ servermetadata - + + diff --git a/snappymail/v/0.0.0/app/templates/Views/Sieve/SpamTestTest.html b/snappymail/v/0.0.0/app/templates/Views/Sieve/SpamTestTest.html index 74c4c1146..6c85bba88 100644 --- a/snappymail/v/0.0.0/app/templates/Views/Sieve/SpamTestTest.html +++ b/snappymail/v/0.0.0/app/templates/Views/Sieve/SpamTestTest.html @@ -1,8 +1,8 @@ spamtest - + diff --git a/snappymail/v/0.0.0/app/templates/Views/Sieve/StringTest.html b/snappymail/v/0.0.0/app/templates/Views/Sieve/StringTest.html index 3eb7a90e6..9c09c1be4 100644 --- a/snappymail/v/0.0.0/app/templates/Views/Sieve/StringTest.html +++ b/snappymail/v/0.0.0/app/templates/Views/Sieve/StringTest.html @@ -1,9 +1,10 @@ string - + + diff --git a/snappymail/v/0.0.0/app/templates/Views/Sieve/VirusTestTest.html b/snappymail/v/0.0.0/app/templates/Views/Sieve/VirusTestTest.html index 1b78bd417..8d7ec6ccd 100644 --- a/snappymail/v/0.0.0/app/templates/Views/Sieve/VirusTestTest.html +++ b/snappymail/v/0.0.0/app/templates/Views/Sieve/VirusTestTest.html @@ -1,7 +1,7 @@ virustest - +