From ff9a89380cd63ef76da83d7ee550dbc1bca7f215 Mon Sep 17 00:00:00 2001 From: the-djmaze <> Date: Thu, 17 Mar 2022 09:03:02 +0100 Subject: [PATCH] Prepare some Sieve restructuring for GUI --- .../{Commands.js => Commands/Actions.js} | 96 +--------- dev/Sieve/Commands/Controls.js | 92 ++++++++++ dev/Sieve/{ => Commands}/Tests.js | 24 +-- dev/Sieve/Extensions/rfc5173.js | 4 +- dev/Sieve/Extensions/rfc5183.js | 4 +- dev/Sieve/Extensions/rfc5229.js | 4 +- dev/Sieve/Extensions/rfc5232.js | 4 +- dev/Sieve/Extensions/rfc5235.js | 6 +- dev/Sieve/Extensions/rfc5260.js | 6 +- dev/Sieve/Extensions/rfc5435.js | 6 +- dev/Sieve/Extensions/rfc5463.js | 4 +- dev/Sieve/Extensions/rfc5490.js | 12 +- dev/Sieve/Extensions/rfc6134.js | 4 +- dev/Sieve/Grammar.js | 166 ++++++++++++------ dev/Sieve/Parser.js | 21 ++- 15 files changed, 259 insertions(+), 194 deletions(-) rename dev/Sieve/{Commands.js => Commands/Actions.js} (50%) create mode 100644 dev/Sieve/Commands/Controls.js rename dev/Sieve/{ => Commands}/Tests.js (93%) diff --git a/dev/Sieve/Commands.js b/dev/Sieve/Commands/Actions.js similarity index 50% rename from dev/Sieve/Commands.js rename to dev/Sieve/Commands/Actions.js index 4214849ba..3d0665a7d 100644 --- a/dev/Sieve/Commands.js +++ b/dev/Sieve/Commands/Actions.js @@ -1,102 +1,20 @@ /** - * https://tools.ietf.org/html/rfc5228#section-2.9 + * https://datatracker.ietf.org/doc/html/rfc5228#section-4 + * Action commands do not take tests or blocks as arguments. */ import { capa } from 'Sieve/Utils'; import { - GrammarCommand, + ActionCommand, GrammarString, - GrammarStringList, GrammarQuotedString } from 'Sieve/Grammar'; -/** - * https://tools.ietf.org/html/rfc5228#section-3.1 - * Usage: - * if - * elsif - * else - */ -export class ConditionalCommand extends GrammarCommand -{ - constructor() - { - super(); - this.test = null; - } - - toString() - { - return this.identifier + ' ' + this.test + ' ' + this.commands; - } -/* - public function pushArguments(array $args): void - { - args.forEach((arg, i) => { - if (':' === args[i-1][0]) { - this[args[i-1].replace(':','_')].value = arg.value; - } - }); - print_r($args); - exit; - } -*/ -} - -export class IfCommand extends ConditionalCommand -{ -} - -export class ElsIfCommand extends ConditionalCommand -{ -} - -export class ElseCommand extends ConditionalCommand -{ - toString() - { - return this.identifier + ' ' + this.commands; - } -} - -/** - * https://tools.ietf.org/html/rfc5228#section-3.2 - */ -export class RequireCommand extends GrammarCommand -{ - constructor() - { - super(); - this.capabilities = new GrammarStringList(); - } - - toString() - { - return 'require ' + this.capabilities + ';'; - } - - pushArguments(args) - { - if (args[0] instanceof GrammarStringList) { - this.capabilities = args[0]; - } else if (args[0] instanceof GrammarQuotedString) { - this.capabilities.push(args[0]); - } - } -} - -/** - * https://tools.ietf.org/html/rfc5228#section-3.3 - */ -export class StopCommand extends GrammarCommand -{ -} - /** * https://tools.ietf.org/html/rfc5228#section-4.1 */ -export class FileIntoCommand extends GrammarCommand +export class FileIntoCommand extends ActionCommand { constructor() { @@ -139,7 +57,7 @@ export class FileIntoCommand extends GrammarCommand /** * https://tools.ietf.org/html/rfc5228#section-4.2 */ -export class RedirectCommand extends GrammarCommand +export class RedirectCommand extends ActionCommand { constructor() { @@ -181,13 +99,13 @@ export class RedirectCommand extends GrammarCommand /** * https://tools.ietf.org/html/rfc5228#section-4.3 */ -export class KeepCommand extends GrammarCommand +export class KeepCommand extends ActionCommand { } /** * https://tools.ietf.org/html/rfc5228#section-4.4 */ -export class DiscardCommand extends GrammarCommand +export class DiscardCommand extends ActionCommand { } diff --git a/dev/Sieve/Commands/Controls.js b/dev/Sieve/Commands/Controls.js new file mode 100644 index 000000000..167f22bad --- /dev/null +++ b/dev/Sieve/Commands/Controls.js @@ -0,0 +1,92 @@ +/** + * https://tools.ietf.org/html/rfc5228#section-2.9 + * A control structure is a control command that ends with a block instead of a semicolon. + */ + +import { + GrammarCommand, + GrammarStringList, + GrammarQuotedString +} from 'Sieve/Grammar'; + +/** + * https://tools.ietf.org/html/rfc5228#section-3.1 + * Usage: + * if + * elsif + * else + */ +export class ConditionalCommand extends GrammarCommand +{ + constructor() + { + super(); + this.test = null; + } + + toString() + { + return this.identifier + ' ' + this.test + ' ' + this.commands; + } +/* + public function pushArguments(array $args): void + { + args.forEach((arg, i) => { + if (':' === args[i-1][0]) { + this[args[i-1].replace(':','_')].value = arg.value; + } + }); + print_r($args); + exit; + } +*/ +} + +export class IfCommand extends ConditionalCommand +{ +} + +export class ElsIfCommand extends ConditionalCommand +{ +} + +export class ElseCommand extends ConditionalCommand +{ + toString() + { + return this.identifier + ' ' + this.commands; + } +} + +/** + * https://tools.ietf.org/html/rfc5228#section-3.2 + */ +export class RequireCommand extends GrammarCommand +{ + constructor() + { + super(); + this.capabilities = new GrammarStringList(); + } + + toString() + { + return 'require ' + this.capabilities + ';'; + } + + pushArguments(args) + { + if (args[0] instanceof GrammarStringList) { + this.capabilities = args[0]; + } else if (args[0] instanceof GrammarQuotedString) { + this.capabilities.push(args[0]); + } + } +} + +/** + * https://tools.ietf.org/html/rfc5228#section-3.3 + */ +export class StopCommand extends GrammarCommand +{ +} diff --git a/dev/Sieve/Tests.js b/dev/Sieve/Commands/Tests.js similarity index 93% rename from dev/Sieve/Tests.js rename to dev/Sieve/Commands/Tests.js index 0397b5478..58791c14e 100644 --- a/dev/Sieve/Tests.js +++ b/dev/Sieve/Commands/Tests.js @@ -8,7 +8,7 @@ import { GrammarNumber, GrammarString, GrammarStringList, - GrammarTest, + TestCommand, GrammarTestList } from 'Sieve/Grammar'; @@ -31,7 +31,7 @@ const /** * https://tools.ietf.org/html/rfc5228#section-5.1 */ -export class AddressTest extends GrammarTest +export class AddressTest extends TestCommand { constructor() { @@ -98,7 +98,7 @@ export class AddressTest extends GrammarTest /** * https://tools.ietf.org/html/rfc5228#section-5.2 */ -export class AllOfTest extends GrammarTest +export class AllOfTest extends TestCommand { constructor() { @@ -115,7 +115,7 @@ export class AllOfTest extends GrammarTest /** * https://tools.ietf.org/html/rfc5228#section-5.3 */ -export class AnyOfTest extends GrammarTest +export class AnyOfTest extends TestCommand { constructor() { @@ -132,7 +132,7 @@ export class AnyOfTest extends GrammarTest /** * https://tools.ietf.org/html/rfc5228#section-5.4 */ -export class EnvelopeTest extends GrammarTest +export class EnvelopeTest extends TestCommand { constructor() { @@ -169,7 +169,7 @@ export class EnvelopeTest extends GrammarTest /** * https://tools.ietf.org/html/rfc5228#section-5.5 */ -export class ExistsTest extends GrammarTest +export class ExistsTest extends TestCommand { constructor() { @@ -214,7 +214,7 @@ export class ExistsTest extends GrammarTest /** * https://tools.ietf.org/html/rfc5228#section-5.6 */ -export class FalseTest extends GrammarTest +export class FalseTest extends TestCommand { toString() { @@ -225,7 +225,7 @@ export class FalseTest extends GrammarTest /** * https://tools.ietf.org/html/rfc5228#section-5.7 */ -export class HeaderTest extends GrammarTest +export class HeaderTest extends TestCommand { constructor() { @@ -304,12 +304,12 @@ export class HeaderTest extends GrammarTest /** * https://tools.ietf.org/html/rfc5228#section-5.8 */ -export class NotTest extends GrammarTest +export class NotTest extends TestCommand { constructor() { super(); - this.test = new GrammarTest; + this.test = new TestCommand; } toString() @@ -326,7 +326,7 @@ export class NotTest extends GrammarTest /** * https://tools.ietf.org/html/rfc5228#section-5.9 */ -export class SizeTest extends GrammarTest +export class SizeTest extends TestCommand { constructor() { @@ -355,7 +355,7 @@ export class SizeTest extends GrammarTest /** * https://tools.ietf.org/html/rfc5228#section-5.10 */ -export class TrueTest extends GrammarTest +export class TrueTest extends TestCommand { toString() { diff --git a/dev/Sieve/Extensions/rfc5173.js b/dev/Sieve/Extensions/rfc5173.js index 5d85fdab6..23a6e9a68 100644 --- a/dev/Sieve/Extensions/rfc5173.js +++ b/dev/Sieve/Extensions/rfc5173.js @@ -5,10 +5,10 @@ import { GrammarString, GrammarStringList, - GrammarTest + TestCommand } from 'Sieve/Grammar'; -export class BodyTest extends GrammarTest +export class BodyTest extends TestCommand { constructor() { diff --git a/dev/Sieve/Extensions/rfc5183.js b/dev/Sieve/Extensions/rfc5183.js index 66d8cbe44..7e8ff1388 100644 --- a/dev/Sieve/Extensions/rfc5183.js +++ b/dev/Sieve/Extensions/rfc5183.js @@ -5,10 +5,10 @@ import { GrammarQuotedString, GrammarStringList, - GrammarTest + TestCommand } from 'Sieve/Grammar'; -export class EnvironmentTest extends GrammarTest +export class EnvironmentTest extends TestCommand { constructor() { diff --git a/dev/Sieve/Extensions/rfc5229.js b/dev/Sieve/Extensions/rfc5229.js index 86e1b7df6..c979b057e 100644 --- a/dev/Sieve/Extensions/rfc5229.js +++ b/dev/Sieve/Extensions/rfc5229.js @@ -6,7 +6,7 @@ import { GrammarCommand, GrammarQuotedString, GrammarStringList, - GrammarTest + TestCommand } from 'Sieve/Grammar'; export class SetCommand extends GrammarCommand @@ -45,7 +45,7 @@ export class SetCommand extends GrammarCommand } } -export class StringTest extends GrammarTest +export class StringTest extends TestCommand { constructor() { diff --git a/dev/Sieve/Extensions/rfc5232.js b/dev/Sieve/Extensions/rfc5232.js index 918c944df..5dcbe05ec 100644 --- a/dev/Sieve/Extensions/rfc5232.js +++ b/dev/Sieve/Extensions/rfc5232.js @@ -7,7 +7,7 @@ import { GrammarQuotedString, GrammarString, GrammarStringList, - GrammarTest + TestCommand } from 'Sieve/Grammar'; class FlagCommand extends GrammarCommand @@ -63,7 +63,7 @@ export class RemoveFlagCommand extends FlagCommand { } -export class HasFlagTest extends GrammarTest +export class HasFlagTest extends TestCommand { constructor() { diff --git a/dev/Sieve/Extensions/rfc5235.js b/dev/Sieve/Extensions/rfc5235.js index 0147159f7..5561a1a5e 100644 --- a/dev/Sieve/Extensions/rfc5235.js +++ b/dev/Sieve/Extensions/rfc5235.js @@ -5,10 +5,10 @@ import { GrammarQuotedString, GrammarString, - GrammarTest + TestCommand } from 'Sieve/Grammar'; -export class SpamTestTest extends GrammarTest +export class SpamTestTest extends TestCommand { constructor() { @@ -41,7 +41,7 @@ export class SpamTestTest extends GrammarTest } } -export class VirusTestTest extends GrammarTest +export class VirusTestTest extends TestCommand { constructor() { diff --git a/dev/Sieve/Extensions/rfc5260.js b/dev/Sieve/Extensions/rfc5260.js index 57980f56f..d54736fa2 100644 --- a/dev/Sieve/Extensions/rfc5260.js +++ b/dev/Sieve/Extensions/rfc5260.js @@ -6,10 +6,10 @@ import { GrammarNumber, GrammarQuotedString, GrammarStringList, - GrammarTest + TestCommand } from 'Sieve/Grammar'; -export class DateTest extends GrammarTest +export class DateTest extends TestCommand { constructor() { @@ -58,7 +58,7 @@ export class DateTest extends GrammarTest } } -export class CurrentDateTest extends GrammarTest +export class CurrentDateTest extends TestCommand { constructor() { diff --git a/dev/Sieve/Extensions/rfc5435.js b/dev/Sieve/Extensions/rfc5435.js index 4c1939456..9c1f0715d 100644 --- a/dev/Sieve/Extensions/rfc5435.js +++ b/dev/Sieve/Extensions/rfc5435.js @@ -7,7 +7,7 @@ import { GrammarNumber, GrammarQuotedString, GrammarStringList, - GrammarTest + TestCommand } from 'Sieve/Grammar'; /** @@ -72,7 +72,7 @@ export class NotifyCommand extends GrammarCommand /** * https://datatracker.ietf.org/doc/html/rfc5435#section-4 */ -export class ValidNotifyMethodTest extends GrammarTest +export class ValidNotifyMethodTest extends TestCommand { constructor() { @@ -94,7 +94,7 @@ export class ValidNotifyMethodTest extends GrammarTest /** * https://datatracker.ietf.org/doc/html/rfc5435#section-5 */ -export class NotifyMethodCapabilityTest extends GrammarTest +export class NotifyMethodCapabilityTest extends TestCommand { constructor() { diff --git a/dev/Sieve/Extensions/rfc5463.js b/dev/Sieve/Extensions/rfc5463.js index 967363d0b..8a49232ea 100644 --- a/dev/Sieve/Extensions/rfc5463.js +++ b/dev/Sieve/Extensions/rfc5463.js @@ -4,7 +4,7 @@ import { GrammarCommand, - GrammarTest, + TestCommand, GrammarQuotedString, GrammarStringList } from 'Sieve/Grammar'; @@ -12,7 +12,7 @@ import { /** * https://datatracker.ietf.org/doc/html/rfc5463#section-4 */ -export class IHaveTest extends GrammarTest +export class IHaveTest extends TestCommand { constructor() { diff --git a/dev/Sieve/Extensions/rfc5490.js b/dev/Sieve/Extensions/rfc5490.js index ca02aeb47..14a087b1d 100644 --- a/dev/Sieve/Extensions/rfc5490.js +++ b/dev/Sieve/Extensions/rfc5490.js @@ -3,7 +3,7 @@ */ import { - GrammarTest, + TestCommand, GrammarQuotedString, GrammarStringList } from 'Sieve/Grammar'; @@ -11,7 +11,7 @@ import { /** * https://datatracker.ietf.org/doc/html/rfc5490#section-3.1 */ -export class MailboxExistsTest extends GrammarTest +export class MailboxExistsTest extends TestCommand { constructor() { @@ -37,7 +37,7 @@ export class MailboxExistsTest extends GrammarTest /** * https://datatracker.ietf.org/doc/html/rfc5490#section-3.3 */ -export class MetadataTest extends GrammarTest +export class MetadataTest extends TestCommand { constructor() { @@ -70,7 +70,7 @@ export class MetadataTest extends GrammarTest /** * https://datatracker.ietf.org/doc/html/rfc5490#section-3.4 */ -export class MetadataExistsTest extends GrammarTest +export class MetadataExistsTest extends TestCommand { constructor() { @@ -98,7 +98,7 @@ export class MetadataExistsTest extends GrammarTest /** * https://datatracker.ietf.org/doc/html/rfc5490#section-3.3 */ -export class ServerMetadataTest extends GrammarTest +export class ServerMetadataTest extends TestCommand { constructor() { @@ -128,7 +128,7 @@ export class ServerMetadataTest extends GrammarTest /** * https://datatracker.ietf.org/doc/html/rfc5490#section-3.4 */ -export class ServerMetadataExistsTest extends GrammarTest +export class ServerMetadataExistsTest extends TestCommand { constructor() { diff --git a/dev/Sieve/Extensions/rfc6134.js b/dev/Sieve/Extensions/rfc6134.js index f3558ad17..872938eab 100644 --- a/dev/Sieve/Extensions/rfc6134.js +++ b/dev/Sieve/Extensions/rfc6134.js @@ -4,13 +4,13 @@ import { GrammarStringList, - GrammarTest + TestCommand } from 'Sieve/Grammar'; /** * https://datatracker.ietf.org/doc/html/rfc6134#section-2.7 */ -export class ValidExtListTest extends GrammarTest +export class ValidExtListTest extends TestCommand { constructor() { diff --git a/dev/Sieve/Grammar.js b/dev/Sieve/Grammar.js index e985b696a..f610c260e 100644 --- a/dev/Sieve/Grammar.js +++ b/dev/Sieve/Grammar.js @@ -56,7 +56,7 @@ export class GrammarCommand { constructor(identifier) { - this.identifier = identifier || this.constructor.name.toLowerCase().replace('command', ''); + this.identifier = identifier || this.constructor.name.toLowerCase().replace(/(test|command|action)$/, ''); this.arguments = []; this.commands = new GrammarCommands; } @@ -105,6 +105,114 @@ export class GrammarCommands extends Array } } +/** + * https://tools.ietf.org/html/rfc5228#section-3 + */ +export class ControlCommand extends GrammarCommand +{ + constructor(identifier) + { + super(identifier); + this.commands = new GrammarCommands; + } + + toString() + { + let result = this.identifier; + if (this.arguments.length) { + result += ' ' + arrayToString(this.arguments, ' '); + } + return result + ( + this.commands.length ? ' ' + this.commands : ';' + ); + } + + getComparators() + { + return ['i;ascii-casemap']; + } + + getMatchTypes() + { + return [':is', ':contains', ':matches']; + } + + pushArguments(args) + { + this.arguments = args; + } +} + +/** + * https://tools.ietf.org/html/rfc5228#section-4 + */ +export class ActionCommand extends GrammarCommand +{ + constructor(identifier) + { + super(identifier); + } + + toString() + { + let result = this.identifier; + if (this.arguments.length) { + result += ' ' + arrayToString(this.arguments, ' '); + } + return result + ';' + } +} + +/** + * https://tools.ietf.org/html/rfc5228#section-5 + */ +export class TestCommand extends GrammarCommand +{ + constructor(identifier) + { + super(identifier); + // Almost every test has a comparator and match_type, so define them here + this.comparator = ''; + this.match_type = ':is'; + } + + 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 : '') + + ' ' + arrayToString(this.arguments, ' ')).trim(); + } +} + +/** + * https://tools.ietf.org/html/rfc5228#section-5.2 + * https://tools.ietf.org/html/rfc5228#section-5.3 + */ +export class GrammarTestList extends Array +{ + toString() + { + if (1 < this.length) { +// return '(\r\n\t' + arrayToString(this, ',\r\n\t') + '\r\n)'; + return '(' + this.join(', ') + ')'; + } + return this.length ? this[0] : ''; + } + + push(value) + { + if (!(value instanceof TestCommand)) { + throw 'Not an instanceof Test'; + } + super.push(value); + } +} + export class GrammarBracketComment extends GrammarComment { toString() @@ -221,59 +329,3 @@ GrammarMultiLine.fromString = string => { } return new GrammarMultiLine(); } - -/** - * https://tools.ietf.org/html/rfc5228#section-5 - */ -export class GrammarTest -{ - constructor(identifier) - { - this.identifier = identifier || this.constructor.name.toLowerCase().replace(/test$/, ''); - // Almost every test has a comparator and match_type, so define them here - this.comparator = '', - this.match_type = ':is', - this.arguments = []; - } - - 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 : '') - + ' ' + arrayToString(this.arguments, ' ')).trim(); - } - - pushArguments(args) - { - this.arguments = args; - } -} - -/** - * https://tools.ietf.org/html/rfc5228#section-5.2 - * https://tools.ietf.org/html/rfc5228#section-5.3 - */ -export class GrammarTestList extends Array -{ - toString() - { - if (1 < this.length) { -// return '(\r\n\t' + arrayToString(this, ',\r\n\t') + '\r\n)'; - return '(' + this.join(', ') + ')'; - } - return this.length ? this[0] : ''; - } - - push(value) - { - if (!(value instanceof GrammarTest)) { - throw 'Not an instanceof Test'; - } - super.push(value); - } -} diff --git a/dev/Sieve/Parser.js b/dev/Sieve/Parser.js index b4508cb81..65e163ae7 100644 --- a/dev/Sieve/Parser.js +++ b/dev/Sieve/Parser.js @@ -23,22 +23,25 @@ import { GrammarNumber, GrammarQuotedString, GrammarStringList, - GrammarTest, + TestCommand, GrammarTestList } from 'Sieve/Grammar'; import { - ConditionalCommand, DiscardCommand, + FileIntoCommand, + KeepCommand, + RedirectCommand +} from 'Sieve/Commands/Actions'; + +import { + ConditionalCommand, ElsIfCommand, ElseCommand, - FileIntoCommand, IfCommand, - KeepCommand, - RedirectCommand, RequireCommand, StopCommand -} from 'Sieve/Commands'; +} from 'Sieve/Commands/Controls'; import { AddressTest, @@ -51,7 +54,7 @@ import { NotTest, SizeTest, TrueTest -} from 'Sieve/Tests'; +} from 'Sieve/Commands/Tests'; import { BodyTest } from 'Sieve/Extensions/rfc5173'; import { EnvironmentTest } from 'Sieve/Extensions/rfc5183'; @@ -265,14 +268,14 @@ export const parseScript = (script, name = 'script.sieve') => { || command instanceof NotTest || command.tests instanceof GrammarTestList)) { console.error('Unknown test: ' + value); - new_command = new GrammarTest(value); + new_command = new TestCommand(value); } else { console.error('Unknown command: ' + value); new_command = new GrammarCommand(value); } } - if (new_command instanceof GrammarTest) { + if (new_command instanceof TestCommand) { if (command instanceof ConditionalCommand || command instanceof NotTest) { // if/elsif/else new_command // not new_command