Added Sieve extension rfc5703

This commit is contained in:
the-djmaze 2022-03-16 12:05:50 +01:00
parent 7468268e1a
commit 63de537f8f
5 changed files with 341 additions and 80 deletions

View file

@ -33,6 +33,11 @@ export class ConditionalCommand extends GrammarCommand
/* /*
public function pushArguments(array $args): void 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); print_r($args);
exit; exit;
} }

View file

@ -42,7 +42,8 @@ export class VacationCommand extends GrammarCommand
result += ' :subject ' + this._subject; result += ' :subject ' + this._subject;
} }
if (this._from.length) { if (this._from.length) {
result += ' :from ' + this.arguments[':from']; result += ' :from ' + this._from;
// result += ' :from ' + this.arguments[':from'];
} }
if (this.addresses.length) { if (this.addresses.length) {
result += ' :addresses ' + this.addresses.toString(); result += ' :addresses ' + this.addresses.toString();

View file

@ -0,0 +1,178 @@
/**
* https://tools.ietf.org/html/rfc5703
*/
import {
GrammarCommand,
GrammarNumber,
GrammarQuotedString,
GrammarString,
GrammarStringList
} from 'Sieve/Grammar';
/**
* https://datatracker.ietf.org/doc/html/rfc5703#section-3
*/
export class ForEveryPartCommand extends GrammarCommand
{
constructor()
{
super();
this._name = new GrammarString;
}
get require() { return 'foreverypart'; }
toString()
{
let result = 'foreverypart';
if (this._subject.length) {
result += ' :name ' + this._name;
}
return result + ' ' + this.commands;
}
pushArguments(args)
{
args.forEach((arg, i) => {
if (':name' === arg) {
this._name.value = args[i+1].value;
}
});
}
}
export class BreakCommand extends ForEveryPartCommand
{
toString()
{
let result = 'break';
if (this._subject.length) {
result += ' :name ' + this._name;
}
return result + ';';
}
}
/**
* https://datatracker.ietf.org/doc/html/rfc5703#section-5
*/
export class ReplaceCommand extends GrammarCommand
{
constructor()
{
super();
this.mime = false;
this._subject = new GrammarQuotedString;
this._from = new GrammarQuotedString;
this.replacement = new GrammarQuotedString;
}
get require() { return 'replace'; }
toString()
{
let result = 'replace';
if (this.mime) {
result += ' :mime';
}
if (this._subject.length) {
result += ' :subject ' + this._subject;
}
if (this._from.length) {
result += ' :from ' + this._from;
// result += ' :from ' + this.arguments[':from'];
}
return result + this.replacement + ';';
}
pushArguments(args)
{
this.replacement = args.pop();
args.forEach((arg, i) => {
if (':mime' === arg) {
this.mime = true;
} else if (':' === args[i-1][0]) {
// :subject, :from
this[args[i-1].replace(':','_')].value = arg.value;
}
});
}
}
/**
* https://datatracker.ietf.org/doc/html/rfc5703#section-6
*/
export class EncloseCommand extends GrammarCommand
{
constructor()
{
super();
this._subject = new GrammarQuotedString;
this.headers = new GrammarStringList;
}
get require() { return 'enclose'; }
toString()
{
let result = 'enclose';
if (this._subject.length) {
result += ' :subject ' + this._subject;
}
if (this.headers.length) {
result += ' :headers ' + this.headers;
}
return result + ' :text;';
}
pushArguments(args)
{
args.forEach((arg, i) => {
if (':' === args[i-1][0]) {
// :subject, :headers
this[args[i-1].replace(':','_')].value = arg.value;
}
});
}
}
/**
* https://datatracker.ietf.org/doc/html/rfc5703#section-7
*/
export class ExtractTextCommand extends GrammarCommand
{
constructor()
{
super();
this.modifiers = [];
this._first = new GrammarNumber;
this.varname = new GrammarQuotedString;
}
get require() { return 'extracttext'; }
toString()
{
let result = 'extracttext '
+ this.modifiers.join(' ');
if (0 < this._first.value) {
result += ' :first ' + this._first;
}
return result + ' ' + this.varname + ';';
}
pushArguments(args)
{
this.varname = args.pop();
[':lower', ':upper', ':lowerfirst', ':upperfirst', ':quotewildcard', ':length'].forEach(modifier => {
args.includes(modifier) && this.modifiers.push(modifier);
});
args.forEach((arg, i) => {
if (':' === args[i-1][0]) {
// :first
this[args[i-1].replace(':','_')].value = arg.value;
}
});
}
}

View file

@ -2,7 +2,7 @@
* https://tools.ietf.org/html/rfc5228#section-8 * https://tools.ietf.org/html/rfc5228#section-8
*/ */
import { capa, forEachObjectEntry } from 'Sieve/Utils'; import { capa } from 'Sieve/Utils';
import { import {
BRACKET_COMMENT, BRACKET_COMMENT,
@ -57,14 +57,7 @@ import { BodyTest } from 'Sieve/Extensions/rfc5173';
import { EnvironmentTest } from 'Sieve/Extensions/rfc5183'; import { EnvironmentTest } from 'Sieve/Extensions/rfc5183';
import { SetCommand, StringTest } from 'Sieve/Extensions/rfc5229'; import { SetCommand, StringTest } from 'Sieve/Extensions/rfc5229';
import { VacationCommand } from 'Sieve/Extensions/rfc5230'; import { VacationCommand } from 'Sieve/Extensions/rfc5230';
import { SetFlagCommand, AddFlagCommand, RemoveFlagCommand, HasFlagTest } from 'Sieve/Extensions/rfc5232';
import {
SetFlagCommand,
AddFlagCommand,
RemoveFlagCommand,
HasFlagTest
} from 'Sieve/Extensions/rfc5232';
import { SpamTestTest, VirusTestTest } from 'Sieve/Extensions/rfc5235'; import { SpamTestTest, VirusTestTest } from 'Sieve/Extensions/rfc5235';
import { DateTest, CurrentDateTest } from 'Sieve/Extensions/rfc5260'; import { DateTest, CurrentDateTest } from 'Sieve/Extensions/rfc5260';
import { AddHeaderCommand, DeleteHeaderCommand } from 'Sieve/Extensions/rfc5293'; import { AddHeaderCommand, DeleteHeaderCommand } from 'Sieve/Extensions/rfc5293';
@ -72,74 +65,81 @@ import { ErejectCommand, RejectCommand } from 'Sieve/Extensions/rfc5429';
import { NotifyCommand, ValidNotifyMethodTest, NotifyMethodCapabilityTest } from 'Sieve/Extensions/rfc5435'; import { NotifyCommand, ValidNotifyMethodTest, NotifyMethodCapabilityTest } from 'Sieve/Extensions/rfc5435';
import { IHaveTest, ErrorCommand } from 'Sieve/Extensions/rfc5463'; import { IHaveTest, ErrorCommand } from 'Sieve/Extensions/rfc5463';
import { MailboxExistsTest, MetadataTest, MetadataExistsTest } from 'Sieve/Extensions/rfc5490'; import { MailboxExistsTest, MetadataTest, MetadataExistsTest } from 'Sieve/Extensions/rfc5490';
import { ForEveryPartCommand, BreakCommand, ReplaceCommand, EncloseCommand, ExtractTextCommand } from 'Sieve/Extensions/rfc5703';
import { IncludeCommand, ReturnCommand } from 'Sieve/Extensions/rfc6609'; import { IncludeCommand, ReturnCommand } from 'Sieve/Extensions/rfc6609';
const const
AllCommands = { AllCommands = [
// Control commands // Control commands
if: IfCommand, IfCommand,
elsif: ElsIfCommand, ElsIfCommand,
else: ElseCommand, ElseCommand,
conditional: ConditionalCommand, ConditionalCommand,
require: RequireCommand, RequireCommand,
stop: StopCommand, StopCommand,
// Action commands // Action commands
discard: DiscardCommand, DiscardCommand,
fileinto: FileIntoCommand, FileIntoCommand,
keep: KeepCommand, KeepCommand,
redirect: RedirectCommand, RedirectCommand,
// Test commands // Test commands
address: AddressTest, AddressTest,
allof: AllOfTest, AllOfTest,
anyof: AnyOfTest, AnyOfTest,
envelope: EnvelopeTest, EnvelopeTest,
exists: ExistsTest, ExistsTest,
false: FalseTest, FalseTest,
header: HeaderTest, HeaderTest,
not: NotTest, NotTest,
size: SizeTest, SizeTest,
true: TrueTest, TrueTest,
// rfc5173 // rfc5173
body: BodyTest, BodyTest,
// rfc5183 // rfc5183
environment: EnvironmentTest, EnvironmentTest,
// rfc5229 // rfc5229
set: SetCommand, SetCommand,
string: StringTest, StringTest,
// rfc5230 // rfc5230
vacation: VacationCommand, VacationCommand,
// rfc5232 // rfc5232
setflag: SetFlagCommand, SetFlagCommand,
addflag: AddFlagCommand, AddFlagCommand,
removeflag: RemoveFlagCommand, RemoveFlagCommand,
hasflag: HasFlagTest, HasFlagTest,
// rfc5235 // rfc5235
spamtest: SpamTestTest, SpamTestTest,
virustest: VirusTestTest, VirusTestTest,
// rfc5260 // rfc5260
date: DateTest, DateTest,
currentdate: CurrentDateTest, CurrentDateTest,
// rfc5293 // rfc5293
AddHeaderCommand, AddHeaderCommand,
DeleteHeaderCommand, DeleteHeaderCommand,
// rfc5429 // rfc5429
ereject: ErejectCommand, ErejectCommand,
reject: RejectCommand, RejectCommand,
// rfc5435 // rfc5435
notify: NotifyCommand, NotifyCommand,
valid_notify_method: ValidNotifyMethodTest, ValidNotifyMethodTest,
notify_method_capability: NotifyMethodCapabilityTest, NotifyMethodCapabilityTest,
// rfc5463 // rfc5463
ihave: IHaveTest, IHaveTest,
error: ErrorCommand, ErrorCommand,
// rfc5490 // rfc5490
mailboxexists: MailboxExistsTest, MailboxExistsTest,
metadata: MetadataTest, MetadataTest,
metadataexists: MetadataExistsTest, MetadataExistsTest,
// rfc5703
ForEveryPartCommand,
BreakCommand,
ReplaceCommand,
EncloseCommand,
ExtractTextCommand,
// rfc6609 // rfc6609
include: IncludeCommand, IncludeCommand,
return: ReturnCommand ReturnCommand
}, ],
T_UNKNOWN = 0, T_UNKNOWN = 0,
T_STRING_LIST = 1, T_STRING_LIST = 1,
@ -182,12 +182,12 @@ export const parseScript = (script, name = 'script.sieve') => {
// Only activate available commands // Only activate available commands
const Commands = {}; const Commands = {};
forEachObjectEntry(AllCommands, (key, cmd) => { AllCommands.forEach(cmd => {
const requires = (new cmd).require; const obj = new cmd, requires = obj.require;
if (!requires if (!requires
|| (Array.isArray(requires) ? requires : [requires]).every(string => capa.includes(string)) || (Array.isArray(requires) ? requires : [requires]).every(string => capa.includes(string))
) { ) {
Commands[key] = cmd; Commands[obj.identifier] = cmd;
} }
}); });
@ -260,13 +260,14 @@ export const parseScript = (script, name = 'script.sieve') => {
} }
new_command = new Commands[value](); new_command = new Commands[value]();
} else { } else {
console.error('Unknown command: ' + value);
if (command && ( if (command && (
command instanceof ConditionalCommand command instanceof ConditionalCommand
|| command instanceof NotTest || command instanceof NotTest
|| command.tests instanceof GrammarTestList)) { || command.tests instanceof GrammarTestList)) {
console.error('Unknown test: ' + value);
new_command = new GrammarTest(value); new_command = new GrammarTest(value);
} else { } else {
console.error('Unknown command: ' + value);
new_command = new GrammarCommand(value); new_command = new GrammarCommand(value);
} }
} }

View file

@ -2,6 +2,8 @@
* https://tools.ietf.org/html/rfc5228#section-5 * https://tools.ietf.org/html/rfc5228#section-5
*/ */
import { capa } from 'Sieve/Utils';
import { import {
GrammarNumber, GrammarNumber,
GrammarString, GrammarString,
@ -13,7 +15,18 @@ import {
const const
isAddressPart = tag => ':localpart' === tag || ':domain' === tag || ':all' === tag || isSubAddressPart(tag), isAddressPart = tag => ':localpart' === tag || ':domain' === tag || ':all' === tag || isSubAddressPart(tag),
// https://tools.ietf.org/html/rfc5233 // https://tools.ietf.org/html/rfc5233
isSubAddressPart = tag => ':user' === tag || ':detail' === tag; isSubAddressPart = tag => ':user' === tag || ':detail' === tag,
asStringList = arg => {
if (arg instanceof GrammarStringList) {
return arg;
}
let args = new GrammarStringList();
if (arg instanceof GrammarString) {
args.push(arg.value);
}
return args;
};
/** /**
* https://tools.ietf.org/html/rfc5228#section-5.1 * https://tools.ietf.org/html/rfc5228#section-5.1
@ -29,18 +42,31 @@ export class AddressTest extends GrammarTest
// rfc5260#section-6 // rfc5260#section-6
// this.index = new GrammarNumber; // this.index = new GrammarNumber;
// this.last = false; // this.last = false;
// rfc5703#section-6
// this.mime
// this.anychild
} }
get require() { get require() {
let requires = []; let requires = [];
isSubAddressPart(this.address_part) && requires.push('subaddress'); isSubAddressPart(this.address_part) && requires.push('subaddress');
(this.last || (this.index && this.index.value)) && requires.push('index'); (this.last || (this.index && this.index.value)) && requires.push('index');
(this.mime || this.anychild) && requires.push('mime');
return requires; return requires;
} }
toString() toString()
{ {
return 'address' let result = 'address';
if (capa.includes('mime')) {
if (this.mime) {
result += ' :mime';
}
if (this.anychild) {
result += ' :anychild';
}
}
return result
// + (this.last ? ' :last' : (this.index.value ? ' :index ' + this.index : '')) // + (this.last ? ' :last' : (this.index.value ? ' :index ' + this.index : ''))
+ (this.comparator ? ' :comparator ' + this.comparator : '') + (this.comparator ? ' :comparator ' + this.comparator : '')
+ ' ' + this.address_part + ' ' + this.address_part
@ -51,16 +77,19 @@ export class AddressTest extends GrammarTest
pushArguments(args) pushArguments(args)
{ {
this.key_list = asStringList(args.pop());
this.header_list = asStringList(args.pop());
args.forEach((arg, i) => { args.forEach((arg, i) => {
if (isAddressPart(arg)) { if (isAddressPart(arg)) {
this.address_part = arg; this.address_part = arg;
} else if (':last' === arg) { } else if (':last' === arg) {
this.last = true; this.last = true;
} else if (':mime' === arg) {
this.mime = true;
} else if (':anychild' === arg) {
this.anychild = true;
} else if (':index' === args[i-1]) { } else if (':index' === args[i-1]) {
this.index.value = arg.value; this.index.value = arg.value;
} else if (arg instanceof GrammarStringList || arg instanceof GrammarString) {
this[args[i+1] ? 'header_list' : 'key_list'] = arg;
// (args[i+1] ? this.header_list : this.key_list) = arg;
} }
}); });
} }
@ -127,12 +156,11 @@ export class EnvelopeTest extends GrammarTest
pushArguments(args) pushArguments(args)
{ {
args.forEach((arg, i) => { this.key_list = asStringList(args.pop());
this.envelope_part = asStringList(args.pop());
args.forEach(arg => {
if (isAddressPart(arg)) { if (isAddressPart(arg)) {
this.address_part = arg; this.address_part = arg;
} else if (arg instanceof GrammarStringList || arg instanceof GrammarString) {
this[args[i+1] ? 'envelope_part' : 'key_list'] = arg;
// (args[i+1] ? this.envelope_part : this.key_list) = arg;
} }
}); });
} }
@ -147,20 +175,39 @@ export class ExistsTest extends GrammarTest
{ {
super(); super();
this.header_names = new GrammarStringList; this.header_names = new GrammarStringList;
// rfc5703#section-6
// this.mime
// this.anychild
}
get require() {
return (this.mime || this.anychild) ? ['mime'] : null;
} }
toString() toString()
{ {
return 'exists ' + this.header_names.toString(); let result = 'exists';
if (capa.includes('mime')) {
if (this.mime) {
result += ' :mime';
}
if (this.anychild) {
result += ' :anychild';
}
}
return result + ' ' + this.header_names.toString();
} }
pushArguments(args) pushArguments(args)
{ {
if (args[0] instanceof GrammarStringList) { this.header_names = asStringList(args.pop());
this.header_names = args; args.forEach(arg => {
} else if (args[0] instanceof GrammarString) { if (':mime' === arg) {
this.header_names.push(args[0].value); this.mime = true;
} } else if (':anychild' === arg) {
this.mime = true;
}
});
} }
} }
@ -189,18 +236,48 @@ export class HeaderTest extends GrammarTest
// rfc5260#section-6 // rfc5260#section-6
// this.index = new GrammarNumber; // this.index = new GrammarNumber;
// this.last = false; // this.last = false;
// rfc5703#section-6
this.mime = false;
this.anychild = false;
// when ":mime" is used:
this.type = false;
this.subtype = false;
this.contenttype = false;
this.param = new GrammarStringList;
} }
get require() { get require() {
let requires = []; let requires = [];
isSubAddressPart(this.address_part) && requires.push('subaddress'); isSubAddressPart(this.address_part) && requires.push('subaddress');
(this.last || (this.index && this.index.value)) && requires.push('index'); (this.last || (this.index && this.index.value)) && requires.push('index');
(this.mime || this.anychild) && requires.push('mime');
return requires; return requires;
} }
toString() toString()
{ {
return 'header' let result = 'header';
if (capa.includes('mime')) {
if (this.mime) {
result += ' :mime';
if (this.type) {
result += ' :type';
}
if (this.subtype) {
result += ' :subtype';
}
if (this.contenttype) {
result += ' :contenttype';
}
if (this.param.length) {
result += ' :param ' + this.param;
}
}
if (this.anychild) {
result += ' :anychild';
}
}
return result
// + (this.last ? ' :last' : (this.index.value ? ' :index ' + this.index : '')) // + (this.last ? ' :last' : (this.index.value ? ' :index ' + this.index : ''))
+ (this.comparator ? ' :comparator ' + this.comparator : '') + (this.comparator ? ' :comparator ' + this.comparator : '')
+ ' ' + this.match_type + ' ' + this.match_type
@ -210,6 +287,8 @@ export class HeaderTest extends GrammarTest
pushArguments(args) pushArguments(args)
{ {
this.key_list = asStringList(args.pop());
this.header_names = asStringList(args.pop());
args.forEach((arg, i) => { args.forEach((arg, i) => {
if (isAddressPart(arg)) { if (isAddressPart(arg)) {
this.address_part = arg; this.address_part = arg;
@ -217,9 +296,6 @@ export class HeaderTest extends GrammarTest
this.last = true; this.last = true;
} else if (':index' === args[i-1]) { } else if (':index' === args[i-1]) {
this.index.value = arg.value; this.index.value = arg.value;
} else if (arg instanceof GrammarStringList || arg instanceof GrammarString) {
this[args[i+1] ? 'header_names' : 'key_list'] = arg;
// (args[i+1] ? this.header_names : this.key_list) = arg;
} }
}); });
} }