From 73e26d388a87835b942713d71faf7bfc90f543cb Mon Sep 17 00:00:00 2001 From: djmaze Date: Fri, 15 Jan 2021 16:09:03 +0100 Subject: [PATCH] Added: rfc5233 Activate: comparator Bugfix: StringList must use .toString() Bugfix: redirect command Bugfix: MultiLine --- dev/Sieve/Commands.js | 9 ++++- dev/Sieve/Extensions/rfc5173.js | 4 +-- dev/Sieve/Extensions/rfc5183.js | 4 +-- dev/Sieve/Extensions/rfc5229.js | 6 ++-- dev/Sieve/Extensions/rfc5230.js | 2 +- dev/Sieve/Extensions/rfc5232.js | 8 ++--- dev/Sieve/Extensions/rfc5235.js | 10 +++--- dev/Sieve/Extensions/rfc5260.js | 8 ++--- dev/Sieve/Extensions/rfc5293.js | 2 +- dev/Sieve/Grammar.js | 19 +++++++++-- dev/Sieve/Parser.js | 6 +++- dev/Sieve/Tests.js | 58 ++++++++++++++++++++------------- 12 files changed, 86 insertions(+), 50 deletions(-) diff --git a/dev/Sieve/Commands.js b/dev/Sieve/Commands.js index b1a4d5300..72ef14d55 100644 --- a/dev/Sieve/Commands.js +++ b/dev/Sieve/Commands.js @@ -156,7 +156,7 @@ class Redirect extends Command toString() { - return 'redirect ' + this.address + ';'; + return 'redirect ' + this._address + ';'; } get address() @@ -168,6 +168,13 @@ class Redirect extends Command { this._address.value = value; } + + pushArguments(args) + { + if (args[0] instanceof Grammar.StringType) { + this._address = args[0]; + } + } } /** diff --git a/dev/Sieve/Extensions/rfc5173.js b/dev/Sieve/Extensions/rfc5173.js index d1ae95a9c..4a802a7bd 100644 --- a/dev/Sieve/Extensions/rfc5173.js +++ b/dev/Sieve/Extensions/rfc5173.js @@ -20,10 +20,10 @@ class Body extends Grammar.Test toString() { return 'body' -// + ' ' + this.comparator + + (this.comparator ? ' :comparator ' + this.comparator : '') + ' ' + this.match_type + ' ' + this.body_transform - + ' ' + this.key_list; + + ' ' + this.key_list.toString(); } pushArguments(args) diff --git a/dev/Sieve/Extensions/rfc5183.js b/dev/Sieve/Extensions/rfc5183.js index e2dfe6649..dd252ff23 100644 --- a/dev/Sieve/Extensions/rfc5183.js +++ b/dev/Sieve/Extensions/rfc5183.js @@ -20,10 +20,10 @@ class Environment extends Grammar.Test toString() { return 'body' -// + ' ' + this.comparator + + (this.comparator ? ' :comparator ' + this.comparator : '') + ' ' + this.match_type + ' ' + this.name - + ' ' + this.key_list; + + ' ' + this.key_list.toString(); } pushArguments(args) diff --git a/dev/Sieve/Extensions/rfc5229.js b/dev/Sieve/Extensions/rfc5229.js index 5d8e1f6de..08bc1d68b 100644 --- a/dev/Sieve/Extensions/rfc5229.js +++ b/dev/Sieve/Extensions/rfc5229.js @@ -55,9 +55,9 @@ class String extends Grammar.Test { return 'string' + ' ' + this.match_type -// + ' ' + this.comparator - + ' ' + this.source - + ' ' + this.key_list; + + (this.comparator ? ' :comparator ' + this.comparator : '') + + ' ' + this.source.toString() + + ' ' + this.key_list.toString(); } pushArguments(args) diff --git a/dev/Sieve/Extensions/rfc5230.js b/dev/Sieve/Extensions/rfc5230.js index e6f7766df..e04af3d03 100644 --- a/dev/Sieve/Extensions/rfc5230.js +++ b/dev/Sieve/Extensions/rfc5230.js @@ -40,7 +40,7 @@ class Vacation extends Grammar.Command result += ' :from ' + this.arguments[':from']; } if (this.addresses.length) { - result += ' :addresses ' + this.addresses; + result += ' :addresses ' + this.addresses.toString(); } if (this.mime) { result += ' :mime'; diff --git a/dev/Sieve/Extensions/rfc5232.js b/dev/Sieve/Extensions/rfc5232.js index ad789ec13..07cdb1d1b 100644 --- a/dev/Sieve/Extensions/rfc5232.js +++ b/dev/Sieve/Extensions/rfc5232.js @@ -19,7 +19,7 @@ class Flag extends Grammar.Command toString() { - return this.identifier + ' ' + this._variablename + ' ' + this.list_of_flags + ';'; + return this.identifier + ' ' + this._variablename + ' ' + this.list_of_flags.toString() + ';'; } get variablename() @@ -86,9 +86,9 @@ class HasFlag extends Grammar.Test { return 'hasflag' + ' ' + this.match_type -// + ' ' + this.comparator - + ' ' + this.variable_list - + ' ' + this.list_of_flags; + + (this.comparator ? ' :comparator ' + this.comparator : '') + + ' ' + this.variable_list.toString() + + ' ' + this.list_of_flags.toString(); } pushArguments(args) diff --git a/dev/Sieve/Extensions/rfc5235.js b/dev/Sieve/Extensions/rfc5235.js index 21378d83c..dcb2a0595 100644 --- a/dev/Sieve/Extensions/rfc5235.js +++ b/dev/Sieve/Extensions/rfc5235.js @@ -16,14 +16,13 @@ class SpamTest extends Grammar.Test } // get require() { return this.percent ? 'spamtestplus' : 'spamtest'; } -// get require() { return /:value|:count/.test(this.match_type) ? 'relational' : ''; } - get require() { return 'spamtestplus'; } + get require() { return /:value|:count/.test(this.match_type) ? ['spamtestplus','relational'] : 'spamtestplus'; } toString() { return 'spamtest' + (this.percent ? ' :percent' : '') -// + ' ' + this.comparator + + (this.comparator ? ' :comparator ' + this.comparator : '') + ' ' + this.match_type + ' ' + this.value; } @@ -48,13 +47,12 @@ class VirusTest extends Grammar.Test this.value = new Grammar.QuotedString; // 1 - 5 } -// get require() { return /:value|:count/.test(this.match_type) ? ['virustest','relational'] : 'virustest'; } - get require() { return 'virustest'; } + get require() { return /:value|:count/.test(this.match_type) ? ['virustest','relational'] : 'virustest'; } toString() { return 'virustest' -// + ' ' + this.comparator + + (this.comparator ? ' :comparator ' + this.comparator : '') + ' ' + this.match_type + ' ' + this.value; } diff --git a/dev/Sieve/Extensions/rfc5260.js b/dev/Sieve/Extensions/rfc5260.js index 806421d44..470597dab 100644 --- a/dev/Sieve/Extensions/rfc5260.js +++ b/dev/Sieve/Extensions/rfc5260.js @@ -29,11 +29,11 @@ class DateTest extends Grammar.Test return 'date' + (this.last ? ' :last' : (this.index.value ? ' :index ' + this.index : '')) + (this.originalzone ? ' :originalzone' : (this.zone.length ? ' :zone ' + this.zone : '')) -// + ' ' + this.comparator + + (this.comparator ? ' :comparator ' + this.comparator : '') + ' ' + this.match_type + ' ' + this.header_name + ' ' + this.date_part - + ' ' + this.key_list; + + ' ' + this.key_list.toString(); } pushArguments(args) @@ -75,10 +75,10 @@ class CurrentDate extends Grammar.Test { return 'date' + (this.zone.length ? ' :zone ' + this.zone : '') -// + ' ' + this.comparator + + (this.comparator ? ' :comparator ' + this.comparator : '') + ' ' + this.match_type + ' ' + this.date_part - + ' ' + this.key_list; + + ' ' + this.key_list.toString(); } pushArguments(args) diff --git a/dev/Sieve/Extensions/rfc5293.js b/dev/Sieve/Extensions/rfc5293.js index ddc201178..08e03e21b 100644 --- a/dev/Sieve/Extensions/rfc5293.js +++ b/dev/Sieve/Extensions/rfc5293.js @@ -53,7 +53,7 @@ class DeleteHeader extends Grammar.Command { return this.identifier + (this.last ? ' :last' : (this.index.value ? ' :index ' + this.index : '')) -// + ' ' + this.comparator + + (this.comparator ? ' :comparator ' + this.comparator : '') + ' ' + this.match_type + ' ' + this.field_name + ' ' + this.value_patterns + ';'; diff --git a/dev/Sieve/Grammar.js b/dev/Sieve/Grammar.js index 70646e4f9..6b8b9233b 100644 --- a/dev/Sieve/Grammar.js +++ b/dev/Sieve/Grammar.js @@ -203,12 +203,22 @@ class MultiLine extends StringType toString() { return 'text:' - + (this.comment ? "#${this.comment}" : '') + "\r\n" -// + \rtrim(this.value, "\r\n") + + (this.comment ? '# ' + this.comment : '') + "\r\n" + + this.value + "\r\n.\r\n"; } } +let MultiLineRegEx = RegExp('text:[ \\t]*(' + RegEx.HASH_COMMENT + ')?\\r\\n' + + '((?:' + RegEx.MULTILINE_LITERAL + '|' + RegEx.MULTILINE_DOTSTART + ')*)' + + '\\.\\r\\n', 'm'); +MultiLine.fromString = string => { + string = string.match(MultiLineRegEx); + if (string[2]) { + return new MultiLine(string[2].replace(/\r\n$/, ''), string[1]); + } + return new MultiLine(); +} /** * https://tools.ietf.org/html/rfc5228#section-5 @@ -226,7 +236,10 @@ class Test toString() { - return (this.identifier + ' ' + Sieve.arrayToString(this.arguments, ' ')).trim(); + return (this.identifier + + (this.comparator ? ' :comparator ' + this.comparator : '') + + (this.match_type ? ' ' + this.match_type : '') + + ' ' + Sieve.arrayToString(this.arguments, ' ')).trim(); } pushArguments(args) diff --git a/dev/Sieve/Parser.js b/dev/Sieve/Parser.js index 7c75cee16..568f9eb72 100644 --- a/dev/Sieve/Parser.js +++ b/dev/Sieve/Parser.js @@ -152,6 +152,9 @@ Sieve.parseScript = (script, name = 'script.sieve') => { (Array.isArray(command.require) ? command.require : [command.require]) .forEach(string => requires.push(string)); } + if (command.comparator) { + requires.push('comparator-' + this.comparator); + } break; } // Arguments @@ -162,7 +165,7 @@ Sieve.parseScript = (script, name = 'script.sieve') => { pushArg(Grammar.StringList.fromString(value)); break; case T_MULTILINE_STRING: - pushArg(new Grammar.MultiLine(value)); + pushArg(Grammar.MultiLine.fromString(value)); break; case T_QUOTED_STRING: pushArg(new Grammar.QuotedString(value.substr(1,value.length-2))); @@ -254,6 +257,7 @@ Sieve.parseScript = (script, name = 'script.sieve') => { line += (value.split('\n').length - 1); // (value.match(/\n/g) || []).length; } + tree.requires = requires; return tree; }; diff --git a/dev/Sieve/Tests.js b/dev/Sieve/Tests.js index 1318a3807..7e43fe023 100644 --- a/dev/Sieve/Tests.js +++ b/dev/Sieve/Tests.js @@ -6,7 +6,11 @@ const Grammar = Sieve.Grammar, Test = Grammar.Test, - StringList = Grammar.StringList; + StringList = Grammar.StringList, + + isAddressPart = tag => ':localpart' === tag || ':domain' === tag || ':all' === tag || isSubAddressPart(tag), + // https://tools.ietf.org/html/rfc5233 + isSubAddressPart = tag => ':user' === tag || ':detail' === tag; /** * https://tools.ietf.org/html/rfc5228#section-5.1 @@ -16,7 +20,7 @@ class Address extends Test constructor() { super('address'); - this.address_part = ':all'; // :localpart | :domain | :all + this.address_part = ':all'; this.header_list = new StringList; this.key_list = new StringList; // rfc5260#section-6 @@ -24,23 +28,28 @@ class Address extends Test // this.last = false; } - get require() { return this.last ? 'index' : ''; } + get require() { + let requires = []; + isSubAddressPart(this.address_part) && requires.push('subaddress'); + (this.last || (this.index && this.index.value)) && requires.push('index'); + return requires; + } toString() { return 'address' // + (this.last ? ' :last' : (this.index.value ? ' :index ' + this.index : '')) -// + ' ' + this.comparator + + (this.comparator ? ' :comparator ' + this.comparator : '') + ' ' + this.address_part + ' ' + this.match_type - + ' ' + this.header_list - + ' ' + this.key_list; + + ' ' + this.header_list.toString() + + ' ' + this.key_list.toString(); } pushArguments(args) { args.forEach((arg, i) => { - if (':localpart' === arg || ':domain' === arg || ':all' === arg) { + if (isAddressPart(arg)) { this.address_part = arg; } else if (':last' === arg) { this.last = true; @@ -67,7 +76,7 @@ class AllOf extends Test toString() { - return 'allof ' + this.tests; + return 'allof ' + this.tests.toString(); } } @@ -84,7 +93,7 @@ class AnyOf extends Test toString() { - return 'anyof ' + this.tests; + return 'anyof ' + this.tests.toString(); } } @@ -96,27 +105,27 @@ class Envelope extends Test constructor() { super('envelope'); - this.address_part = ':all'; // :localpart | :domain | :all + this.address_part = ':all'; this.envelope_part = new StringList; this.key_list = new StringList; } - get require() { return 'envelope'; } + get require() { return isSubAddressPart(this.address_part) ? ['envelope','subaddress'] : 'envelope'; } toString() { return 'envelope' -// + ' ' + this.comparator + + (this.comparator ? ' :comparator ' + this.comparator : '') + ' ' + this.address_part + ' ' + this.match_type - + ' ' + this.envelope_part - + ' ' + this.key_list; + + ' ' + this.envelope_part.toString() + + ' ' + this.key_list.toString(); } pushArguments(args) { args.forEach((arg, i) => { - if (':localpart' === arg || ':domain' === arg || ':all' === arg) { + if (isAddressPart(arg)) { this.address_part = arg; } else if (arg instanceof StringList || arg instanceof Grammar.StringType) { this[args[i+1] ? 'envelope_part' : 'key_list'] = arg; @@ -139,7 +148,7 @@ class Exists extends Test toString() { - return 'exists ' + this.header_names; + return 'exists ' + this.header_names.toString(); } pushArguments(args) @@ -171,7 +180,7 @@ class Header extends Test constructor() { super('header'); - this.address_part = ':all'; // :localpart | :domain | :all + this.address_part = ':all'; this.header_names = new StringList; this.key_list = new StringList; // rfc5260#section-6 @@ -179,22 +188,27 @@ class Header extends Test // this.last = false; } - get require() { return this.last ? 'index' : ''; } + get require() { + let requires = []; + isSubAddressPart(this.address_part) && requires.push('subaddress'); + (this.last || (this.index && this.index.value)) && requires.push('index'); + return requires; + } toString() { return 'header' // + (this.last ? ' :last' : (this.index.value ? ' :index ' + this.index : '')) -// + ' ' + this.comparator + + (this.comparator ? ' :comparator ' + this.comparator : '') + ' ' + this.match_type - + ' ' + this.header_names - + ' ' + this.key_list; + + ' ' + this.header_names.toString() + + ' ' + this.key_list.toString(); } pushArguments(args) { args.forEach((arg, i) => { - if (':localpart' === arg || ':domain' === arg || ':all' === arg) { + if (isAddressPart(arg)) { this.address_part = arg; } else if (':last' === arg) { this.last = true;