Get most valid Sieve scripts parsed properly

This commit is contained in:
the-djmaze 2024-09-24 01:37:04 +02:00
parent c254025d7a
commit 76ade5eac0
25 changed files with 175 additions and 71 deletions

View file

@ -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;
}

View file

@ -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;
}

View file

@ -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;
}

View file

@ -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;
}

View file

@ -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;
}

View file

@ -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;
}

View file

@ -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;
}

View file

@ -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;

View file

@ -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;
}

View file

@ -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();
}
}

View file

@ -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) {

View file

@ -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

View file

@ -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;
};