sievelib-ts 0.0.0-stage → 0.1.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +3 -0
- package/README.md +190 -2
- package/dist/commands.cjs +419 -0
- package/dist/commands.cjs.map +1 -0
- package/dist/commands.d.cts +1330 -0
- package/dist/commands.d.ts +1330 -0
- package/dist/commands.js +346 -0
- package/dist/commands.js.map +1 -0
- package/dist/factory.cjs +579 -0
- package/dist/factory.cjs.map +1 -0
- package/dist/factory.d.cts +49 -0
- package/dist/factory.d.ts +49 -0
- package/dist/factory.js +551 -0
- package/dist/factory.js.map +1 -0
- package/dist/index.cjs +1199 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.cts +7 -0
- package/dist/index.d.ts +7 -0
- package/dist/index.js +1103 -0
- package/dist/index.js.map +1 -0
- package/dist/managesieve.cjs +360 -0
- package/dist/managesieve.cjs.map +1 -0
- package/dist/managesieve.d.cts +68 -0
- package/dist/managesieve.d.ts +68 -0
- package/dist/managesieve.js +322 -0
- package/dist/managesieve.js.map +1 -0
- package/dist/parser.cjs +485 -0
- package/dist/parser.cjs.map +1 -0
- package/dist/parser.d.cts +35 -0
- package/dist/parser.d.ts +35 -0
- package/dist/parser.js +456 -0
- package/dist/parser.js.map +1 -0
- package/dist/tools.cjs +41 -0
- package/dist/tools.cjs.map +1 -0
- package/dist/tools.d.cts +6 -0
- package/dist/tools.d.ts +6 -0
- package/dist/tools.js +13 -0
- package/dist/tools.js.map +1 -0
- package/package.json +16 -4
package/LICENSE
ADDED
package/README.md
CHANGED
|
@@ -1,3 +1,191 @@
|
|
|
1
|
-
#
|
|
1
|
+
# sievelib-ts
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
Client-side Sieve and ManageSieve library for Node.js, written in TypeScript.
|
|
4
|
+
|
|
5
|
+
- [Sieve: An Email Filtering Language (RFC 5228)](https://datatracker.ietf.org/doc/html/rfc5228)
|
|
6
|
+
- [ManageSieve (RFC 5804)](https://datatracker.ietf.org/doc/html/rfc5804)
|
|
7
|
+
|
|
8
|
+
`sievelib-ts` has its own npm versioning. Its behavior is based on the sibling
|
|
9
|
+
Python [`sievelib`](https://github.com/tonioo/sievelib) source at commit
|
|
10
|
+
[`7adda3b6`](https://github.com/tonioo/sievelib/commit/7adda3b66690c1d3f2cb55ce14d186ab0715c77a), rather than on a matching PyPI version number.
|
|
11
|
+
|
|
12
|
+
## Installation
|
|
13
|
+
|
|
14
|
+
```bash
|
|
15
|
+
npm install sievelib-ts
|
|
16
|
+
```
|
|
17
|
+
|
|
18
|
+
The package supports Node.js 20 or later and provides both ESM and CommonJS
|
|
19
|
+
exports.
|
|
20
|
+
|
|
21
|
+
```js
|
|
22
|
+
// ESM
|
|
23
|
+
import { Parser, FiltersSet, Client } from "sievelib-ts";
|
|
24
|
+
|
|
25
|
+
// CommonJS
|
|
26
|
+
const { Parser, FiltersSet, Client } = require("sievelib-ts");
|
|
27
|
+
```
|
|
28
|
+
|
|
29
|
+
## Sieve tools
|
|
30
|
+
|
|
31
|
+
### Supported syntax
|
|
32
|
+
|
|
33
|
+
The parser supports the Sieve features and extensions implemented by the
|
|
34
|
+
reference Python project, including Copy, Body, Vacation and
|
|
35
|
+
Vacation Seconds, Relational, Imap4flags, Regex, Notifications, Environment,
|
|
36
|
+
Date, and Variables. `encoded-character` syntax from RFC 5228 section 2.4.2.4
|
|
37
|
+
is intentionally not supported.
|
|
38
|
+
|
|
39
|
+
Extensions must be declared with `require` before their commands or match tags
|
|
40
|
+
are used. The parser validates this requirement.
|
|
41
|
+
|
|
42
|
+
### Parse and inspect a script
|
|
43
|
+
|
|
44
|
+
```js
|
|
45
|
+
import { Parser } from "sievelib-ts";
|
|
46
|
+
|
|
47
|
+
const parser = new Parser();
|
|
48
|
+
|
|
49
|
+
if (!parser.parse('require ["fileinto"];\nfileinto "Archive";')) {
|
|
50
|
+
console.error(parser.error);
|
|
51
|
+
} else {
|
|
52
|
+
console.log(parser.dump());
|
|
53
|
+
console.log(parser.toSieve());
|
|
54
|
+
|
|
55
|
+
// Commands form a mutable AST. `walk()` visits every nested node.
|
|
56
|
+
for (const command of parser.result.flatMap((root) => [...root.walk()])) {
|
|
57
|
+
console.log(command.name, command.type);
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
```
|
|
61
|
+
|
|
62
|
+
`parse()` accepts a JavaScript string or `Uint8Array`, returns `true` or
|
|
63
|
+
`false`, and sets `error` and `errorPos` on failure. The parsed top-level
|
|
64
|
+
commands are available as `parser.result`.
|
|
65
|
+
|
|
66
|
+
### Create filters
|
|
67
|
+
|
|
68
|
+
`FiltersSet` provides the high-level builder API. Header names that are not a
|
|
69
|
+
supported test name are treated as `header` tests.
|
|
70
|
+
|
|
71
|
+
```js
|
|
72
|
+
import { FiltersSet } from "sievelib-ts";
|
|
73
|
+
|
|
74
|
+
const filters = new FiltersSet("mail-rules");
|
|
75
|
+
filters.addfilter(
|
|
76
|
+
"invoice forwarding",
|
|
77
|
+
[["Subject", ":contains", "Invoice"]],
|
|
78
|
+
[["redirect", "billing@example.com"]],
|
|
79
|
+
);
|
|
80
|
+
|
|
81
|
+
console.log(filters.toSieve());
|
|
82
|
+
// # Filter: invoice forwarding
|
|
83
|
+
// if anyof (header :contains "Subject" "Invoice") {
|
|
84
|
+
// redirect "billing@example.com";
|
|
85
|
+
// }
|
|
86
|
+
```
|
|
87
|
+
|
|
88
|
+
The builder automatically adds extension requirements where necessary. It
|
|
89
|
+
supports `anyof` (the default) and `allof`, negated conditions such as
|
|
90
|
+
`:notcontains`, and explicit header conditions when a header name collides with
|
|
91
|
+
a test name:
|
|
92
|
+
|
|
93
|
+
```js
|
|
94
|
+
filters.addfilter(
|
|
95
|
+
"header named size",
|
|
96
|
+
[["header", "size", ":is", "large"]],
|
|
97
|
+
[["fileinto", "Large"]],
|
|
98
|
+
);
|
|
99
|
+
```
|
|
100
|
+
|
|
101
|
+
### Update existing filters
|
|
102
|
+
|
|
103
|
+
Load a parsed script into a filter set to retain `# Filter:` and
|
|
104
|
+
`# Description:` metadata. Rules with no filter comment are named
|
|
105
|
+
`Unnamed rule 1`, `Unnamed rule 2`, and so on.
|
|
106
|
+
|
|
107
|
+
```js
|
|
108
|
+
import { FiltersSet, Parser } from "sievelib-ts";
|
|
109
|
+
|
|
110
|
+
const parser = new Parser();
|
|
111
|
+
parser.parse(`require ["fileinto"];
|
|
112
|
+
if header :contains "Subject" "invoice" { fileinto "Invoices"; }
|
|
113
|
+
`);
|
|
114
|
+
|
|
115
|
+
const filters = new FiltersSet("mail-rules");
|
|
116
|
+
filters.fromParserResult(parser);
|
|
117
|
+
|
|
118
|
+
filters.updatefilter(
|
|
119
|
+
"Unnamed rule 1",
|
|
120
|
+
"invoices",
|
|
121
|
+
[["Subject", ":contains", "invoice"]],
|
|
122
|
+
[["fileinto", "Invoices"]],
|
|
123
|
+
);
|
|
124
|
+
|
|
125
|
+
console.log(filters.get_filter_conditions("invoices"));
|
|
126
|
+
console.log(filters.get_filter_actions("invoices"));
|
|
127
|
+
```
|
|
128
|
+
|
|
129
|
+
Use `disablefilter`, `enablefilter`, `movefilter`, `removefilter`, and
|
|
130
|
+
`replacefilter` to manage a filter set. Disabled filters are represented as an
|
|
131
|
+
`if false` wrapper, rather than comments, so the original AST remains usable.
|
|
132
|
+
|
|
133
|
+
To use another filter-comment convention, configure the prefixes:
|
|
134
|
+
|
|
135
|
+
```js
|
|
136
|
+
const roundcubeFilters = new FiltersSet("roundcube", "# rule:");
|
|
137
|
+
```
|
|
138
|
+
|
|
139
|
+
## ManageSieve tools
|
|
140
|
+
|
|
141
|
+
`Client` is an asynchronous Node.js ManageSieve client. It supports mandatory
|
|
142
|
+
protocol commands, `RENAMESCRIPT` (with a fallback for servers without the
|
|
143
|
+
extension), PLAIN, LOGIN, OAUTHBEARER, and XOAUTH2 authentication, STARTTLS,
|
|
144
|
+
and implicit TLS.
|
|
145
|
+
|
|
146
|
+
```js
|
|
147
|
+
import { Client } from "sievelib-ts";
|
|
148
|
+
|
|
149
|
+
const client = new Client("mail.example.com", 4190);
|
|
150
|
+
|
|
151
|
+
try {
|
|
152
|
+
const authenticated = await client.connect(
|
|
153
|
+
"username",
|
|
154
|
+
"password",
|
|
155
|
+
"", // authorization identity
|
|
156
|
+
true, // request STARTTLS
|
|
157
|
+
false, // implicit TLS instead of STARTTLS
|
|
158
|
+
"PLAIN",
|
|
159
|
+
);
|
|
160
|
+
|
|
161
|
+
if (!authenticated) {
|
|
162
|
+
throw new Error(`${client.errcode}: ${client.errmsg}`);
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
const scripts = await client.listscripts();
|
|
166
|
+
console.log(scripts); // [activeScript, otherScripts]
|
|
167
|
+
|
|
168
|
+
await client.putscript("mail-rules", 'require ["fileinto"];\nkeep;\n');
|
|
169
|
+
await client.setactive("mail-rules");
|
|
170
|
+
console.log(await client.getscript("mail-rules"));
|
|
171
|
+
} finally {
|
|
172
|
+
if (client.authenticated) await client.logout();
|
|
173
|
+
else client.socket?.destroy();
|
|
174
|
+
}
|
|
175
|
+
```
|
|
176
|
+
|
|
177
|
+
All network methods return promises. Failed protocol commands return `false` or
|
|
178
|
+
`null` as appropriate and populate `client.errcode` and `client.errmsg`.
|
|
179
|
+
Arguments containing carriage returns, line feeds, or NUL bytes are rejected to
|
|
180
|
+
prevent ManageSieve command injection.
|
|
181
|
+
|
|
182
|
+
## Development and publishing
|
|
183
|
+
|
|
184
|
+
```bash
|
|
185
|
+
npm run verify
|
|
186
|
+
npm pack --dry-run
|
|
187
|
+
```
|
|
188
|
+
|
|
189
|
+
`npm run verify` runs type checking, tests, and the dual ESM/CommonJS build.
|
|
190
|
+
The `prepublishOnly` hook runs the same verification and validates the npm
|
|
191
|
+
tarball before publication.
|
|
@@ -0,0 +1,419 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __defProp = Object.defineProperty;
|
|
3
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
4
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
5
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
6
|
+
var __export = (target, all) => {
|
|
7
|
+
for (var name in all)
|
|
8
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
9
|
+
};
|
|
10
|
+
var __copyProps = (to, from, except, desc) => {
|
|
11
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
12
|
+
for (let key of __getOwnPropNames(from))
|
|
13
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
14
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
15
|
+
}
|
|
16
|
+
return to;
|
|
17
|
+
};
|
|
18
|
+
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
19
|
+
|
|
20
|
+
// src/commands.ts
|
|
21
|
+
var commands_exports = {};
|
|
22
|
+
__export(commands_exports, {
|
|
23
|
+
ActionCommand: () => ActionCommand,
|
|
24
|
+
AddflagCommand: () => AddflagCommand,
|
|
25
|
+
AddressCommand: () => AddressCommand,
|
|
26
|
+
AllofCommand: () => AllofCommand,
|
|
27
|
+
AnyofCommand: () => AnyofCommand,
|
|
28
|
+
BadArgument: () => BadArgument,
|
|
29
|
+
BadValue: () => BadValue,
|
|
30
|
+
BodyCommand: () => BodyCommand,
|
|
31
|
+
Command: () => Command,
|
|
32
|
+
CommandError: () => CommandError,
|
|
33
|
+
ControlCommand: () => ControlCommand,
|
|
34
|
+
CurrentdateCommand: () => CurrentdateCommand,
|
|
35
|
+
DateCommand: () => DateCommand,
|
|
36
|
+
DiscardCommand: () => DiscardCommand,
|
|
37
|
+
ElseCommand: () => ElseCommand,
|
|
38
|
+
ElsifCommand: () => ElsifCommand,
|
|
39
|
+
EnvelopeCommand: () => EnvelopeCommand,
|
|
40
|
+
EnvironmentCommand: () => EnvironmentCommand,
|
|
41
|
+
ExistsCommand: () => ExistsCommand,
|
|
42
|
+
ExtensionNotLoaded: () => ExtensionNotLoaded,
|
|
43
|
+
FalseCommand: () => FalseCommand,
|
|
44
|
+
FileintoCommand: () => FileintoCommand,
|
|
45
|
+
HasflagCommand: () => HasflagCommand,
|
|
46
|
+
HeaderCommand: () => HeaderCommand,
|
|
47
|
+
IfCommand: () => IfCommand,
|
|
48
|
+
KeepCommand: () => KeepCommand,
|
|
49
|
+
NotCommand: () => NotCommand,
|
|
50
|
+
NotifyCommand: () => NotifyCommand,
|
|
51
|
+
RedirectCommand: () => RedirectCommand,
|
|
52
|
+
RejectCommand: () => RejectCommand,
|
|
53
|
+
RemoveflagCommand: () => RemoveflagCommand,
|
|
54
|
+
RequireCommand: () => RequireCommand,
|
|
55
|
+
SetCommand: () => SetCommand,
|
|
56
|
+
SetflagCommand: () => SetflagCommand,
|
|
57
|
+
SizeCommand: () => SizeCommand,
|
|
58
|
+
StopCommand: () => StopCommand,
|
|
59
|
+
TestCommand: () => TestCommand,
|
|
60
|
+
TrueCommand: () => TrueCommand,
|
|
61
|
+
UnknownCommand: () => UnknownCommand,
|
|
62
|
+
VacationCommand: () => VacationCommand,
|
|
63
|
+
addCommands: () => addCommands,
|
|
64
|
+
add_commands: () => add_commands,
|
|
65
|
+
getCommandInstance: () => getCommandInstance,
|
|
66
|
+
get_command_instance: () => get_command_instance,
|
|
67
|
+
loadedExtensions: () => loadedExtensions,
|
|
68
|
+
parser_state: () => parser_state,
|
|
69
|
+
resetExtensions: () => resetExtensions
|
|
70
|
+
});
|
|
71
|
+
module.exports = __toCommonJS(commands_exports);
|
|
72
|
+
|
|
73
|
+
// src/tools.ts
|
|
74
|
+
var quoted = /^"(?:[^"\\]|\\.)*"$/s;
|
|
75
|
+
var isQuoted = (value) => quoted.test(value);
|
|
76
|
+
var quote = (value) => `"${value.replace(/\\/g, "\\\\").replace(/"/g, '\\"')}"`;
|
|
77
|
+
var unquote = (value) => isQuoted(value) ? value.slice(1, -1).replace(/\\(.)/gs, "$1") : value;
|
|
78
|
+
var unquoteArg = (value) => Array.isArray(value) ? value.map(unquote) : unquote(value);
|
|
79
|
+
|
|
80
|
+
// src/commands.ts
|
|
81
|
+
var CommandError = class extends Error {
|
|
82
|
+
};
|
|
83
|
+
var UnknownCommand = class extends CommandError {
|
|
84
|
+
constructor(name) {
|
|
85
|
+
super(`unknown command '${name}'`);
|
|
86
|
+
}
|
|
87
|
+
};
|
|
88
|
+
var BadArgument = class extends CommandError {
|
|
89
|
+
constructor(command, seen, expected) {
|
|
90
|
+
super(`bad argument ${String(seen)} for command ${command} (${String(expected)} expected)`);
|
|
91
|
+
}
|
|
92
|
+
};
|
|
93
|
+
var BadValue = class extends CommandError {
|
|
94
|
+
constructor(arg, value) {
|
|
95
|
+
super(`bad value ${String(value)} for argument ${arg}`);
|
|
96
|
+
}
|
|
97
|
+
};
|
|
98
|
+
var ExtensionNotLoaded = class extends CommandError {
|
|
99
|
+
constructor(name) {
|
|
100
|
+
super(`extension '${name}' not loaded`);
|
|
101
|
+
}
|
|
102
|
+
};
|
|
103
|
+
var loadedExtensions = [];
|
|
104
|
+
function resetExtensions() {
|
|
105
|
+
loadedExtensions = [];
|
|
106
|
+
}
|
|
107
|
+
var comparator = { name: "comparator", type: ["tag"], values: [":comparator"], extraArg: { type: ["string"], values: ['"i;octet"', '"i;ascii-casemap"'] } };
|
|
108
|
+
var addressPart = { name: "address-part", type: ["tag"], values: [":localpart", ":domain", ":all"] };
|
|
109
|
+
var matchType = { name: "match-type", type: ["tag"], values: [":is", ":contains", ":matches"], extensionValues: { ":count": "relational", ":value": "relational", ":regex": "regex" }, extraArg: { type: ["string"], values: ['"gt"', '"ge"', '"lt"', '"le"', '"eq"', '"ne"'], validFor: [":count", ":value"] } };
|
|
110
|
+
var Command = class _Command {
|
|
111
|
+
static argsDefinition = [];
|
|
112
|
+
static type = "action";
|
|
113
|
+
static acceptChildren = false;
|
|
114
|
+
static variableArgs = false;
|
|
115
|
+
static nonDeterministic = false;
|
|
116
|
+
static extension;
|
|
117
|
+
static mustFollow;
|
|
118
|
+
parent;
|
|
119
|
+
arguments = {};
|
|
120
|
+
extraArguments = {};
|
|
121
|
+
children = [];
|
|
122
|
+
hashComments = [];
|
|
123
|
+
nextArgPos = 0;
|
|
124
|
+
requiredArgs = -1;
|
|
125
|
+
requiredCount = 0;
|
|
126
|
+
curArg;
|
|
127
|
+
constructor(parent) {
|
|
128
|
+
this.parent = parent;
|
|
129
|
+
}
|
|
130
|
+
get name() {
|
|
131
|
+
return this.constructor.name.replace(/Command$/, "").toLowerCase();
|
|
132
|
+
}
|
|
133
|
+
get defs() {
|
|
134
|
+
return this.constructor.argsDefinition;
|
|
135
|
+
}
|
|
136
|
+
get type() {
|
|
137
|
+
return this.constructor.type;
|
|
138
|
+
}
|
|
139
|
+
get acceptChildren() {
|
|
140
|
+
return this.constructor.acceptChildren;
|
|
141
|
+
}
|
|
142
|
+
get variableArgs() {
|
|
143
|
+
return this.constructor.variableArgs;
|
|
144
|
+
}
|
|
145
|
+
get nonDeterministic() {
|
|
146
|
+
return this.constructor.nonDeterministic;
|
|
147
|
+
}
|
|
148
|
+
get extension() {
|
|
149
|
+
return this.constructor.extension;
|
|
150
|
+
}
|
|
151
|
+
get mustFollow() {
|
|
152
|
+
return this.constructor.mustFollow;
|
|
153
|
+
}
|
|
154
|
+
repr() {
|
|
155
|
+
return `${this.name} (type: ${this.type})`;
|
|
156
|
+
}
|
|
157
|
+
addchild(child) {
|
|
158
|
+
if (!this.acceptChildren) return false;
|
|
159
|
+
this.children.push(child);
|
|
160
|
+
return true;
|
|
161
|
+
}
|
|
162
|
+
completeCb() {
|
|
163
|
+
if (this.name === "require") {
|
|
164
|
+
const a = this.arguments.capabilities;
|
|
165
|
+
for (const v of Array.isArray(a) ? a : [a]) {
|
|
166
|
+
const x = unquote(v);
|
|
167
|
+
if (!loadedExtensions.includes(x)) loadedExtensions.push(x);
|
|
168
|
+
}
|
|
169
|
+
}
|
|
170
|
+
}
|
|
171
|
+
reassignArguments() {
|
|
172
|
+
const defs = this.defs;
|
|
173
|
+
if (this.nonDeterministic && defs.length === 2 && defs[0].name in this.arguments && !(defs[1].name in this.arguments)) {
|
|
174
|
+
this.arguments[defs[1].name] = this.arguments[defs[0].name];
|
|
175
|
+
delete this.arguments[defs[0].name];
|
|
176
|
+
this.requiredCount = 1;
|
|
177
|
+
}
|
|
178
|
+
}
|
|
179
|
+
iscomplete(type, value) {
|
|
180
|
+
if (this.variableArgs) return false;
|
|
181
|
+
if (this.requiredArgs < 0) this.requiredArgs = this.defs.filter((x) => x.required).length;
|
|
182
|
+
return (!this.curArg || !this.curArg.extraArg || this.curArg.extraArg.validFor && type && this.curArg.extraArg.type.includes(type) && !this.curArg.extraArg.validFor.includes(String(value))) && this.requiredCount === this.requiredArgs;
|
|
183
|
+
}
|
|
184
|
+
validValue(arg, value, check = true) {
|
|
185
|
+
const v = String(value).toLowerCase();
|
|
186
|
+
if (!arg.values && !arg.extensionValues) return true;
|
|
187
|
+
if (arg.values?.includes(v)) return true;
|
|
188
|
+
const e = arg.extensionValues?.[v];
|
|
189
|
+
if (e) {
|
|
190
|
+
if (check && !loadedExtensions.includes(e)) throw new ExtensionNotLoaded(e);
|
|
191
|
+
return true;
|
|
192
|
+
}
|
|
193
|
+
return false;
|
|
194
|
+
}
|
|
195
|
+
checkNextArg(type, value, add = true, checkExtension = true) {
|
|
196
|
+
if (!this.defs.length || this.iscomplete(type, value) && this.nextArgPos >= this.defs.length) return false;
|
|
197
|
+
if (this.curArg?.extraArg) {
|
|
198
|
+
const e = this.curArg.extraArg;
|
|
199
|
+
if (e.type.includes(type) && (!e.values || e.values.includes(String(value)))) {
|
|
200
|
+
if (add) this.extraArguments[this.curArg.name] = value;
|
|
201
|
+
this.curArg = void 0;
|
|
202
|
+
return true;
|
|
203
|
+
}
|
|
204
|
+
throw new BadValue(this.curArg.name, value);
|
|
205
|
+
}
|
|
206
|
+
for (let pos = this.nextArgPos; pos < this.defs.length; pos++) {
|
|
207
|
+
const arg = this.defs[pos];
|
|
208
|
+
if (arg.required) {
|
|
209
|
+
if (arg.type[0] === "testlist") {
|
|
210
|
+
if (type !== "test") throw new BadArgument(this.name, value, arg.type);
|
|
211
|
+
if (add) (this.arguments[arg.name] ??= []).push(value);
|
|
212
|
+
return true;
|
|
213
|
+
}
|
|
214
|
+
if (!arg.type.includes(type) && !(type === "string" && arg.type.includes("stringlist"))) throw new BadArgument(this.name, value, arg.type);
|
|
215
|
+
if (!this.validValue(arg, value, checkExtension)) throw new BadArgument(this.name, value, arg.type);
|
|
216
|
+
this.curArg = arg;
|
|
217
|
+
this.requiredCount++;
|
|
218
|
+
this.nextArgPos = pos + 1;
|
|
219
|
+
if (add) this.arguments[arg.name] = value;
|
|
220
|
+
return true;
|
|
221
|
+
}
|
|
222
|
+
if (arg.type.includes(type) && this.validValue(arg, value, checkExtension)) {
|
|
223
|
+
if (arg.extension && checkExtension && !loadedExtensions.includes(arg.extension)) throw new ExtensionNotLoaded(arg.extension);
|
|
224
|
+
if (arg.type.includes("tag") && arg.name in this.arguments) throw new BadArgument(this.name, value, `a single ${arg.name}`);
|
|
225
|
+
if (arg.extraArg && (!arg.extraArg.validFor || arg.extraArg.validFor.includes(String(value)))) this.curArg = arg;
|
|
226
|
+
if (add) this.arguments[arg.name] = value;
|
|
227
|
+
if (!arg.type.includes("tag")) this.nextArgPos = pos + 1;
|
|
228
|
+
return true;
|
|
229
|
+
}
|
|
230
|
+
}
|
|
231
|
+
return false;
|
|
232
|
+
}
|
|
233
|
+
argsAsTuple() {
|
|
234
|
+
const out = [this.name];
|
|
235
|
+
for (const d of this.defs) {
|
|
236
|
+
if (!(d.name in this.arguments)) continue;
|
|
237
|
+
let v = this.arguments[d.name];
|
|
238
|
+
let t = d.type;
|
|
239
|
+
if (t.includes("tag")) {
|
|
240
|
+
out.push(v);
|
|
241
|
+
if (!(d.name in this.extraArguments)) continue;
|
|
242
|
+
v = this.extraArguments[d.name];
|
|
243
|
+
t = d.extraArg.type;
|
|
244
|
+
}
|
|
245
|
+
out.push(t.includes("string") || t.includes("stringlist") ? unquoteArg(v) : v);
|
|
246
|
+
}
|
|
247
|
+
return out;
|
|
248
|
+
}
|
|
249
|
+
args_as_tuple() {
|
|
250
|
+
return this.argsAsTuple();
|
|
251
|
+
}
|
|
252
|
+
walk() {
|
|
253
|
+
const self = this;
|
|
254
|
+
return (function* () {
|
|
255
|
+
yield self;
|
|
256
|
+
for (const d of self.defs) {
|
|
257
|
+
const v = self.arguments[d.name];
|
|
258
|
+
if (Array.isArray(v) && d.type[0] === "testlist") for (const c of v) yield* c.walk();
|
|
259
|
+
else if (v instanceof _Command) yield* v.walk();
|
|
260
|
+
}
|
|
261
|
+
for (const c of self.children) yield* c.walk();
|
|
262
|
+
})();
|
|
263
|
+
}
|
|
264
|
+
toSieve(indent = 0) {
|
|
265
|
+
let s = `${" ".repeat(indent)}${this.name}`;
|
|
266
|
+
for (const d of this.defs) {
|
|
267
|
+
if (!(d.name in this.arguments)) continue;
|
|
268
|
+
let v = this.arguments[d.name];
|
|
269
|
+
let t = d.type;
|
|
270
|
+
if (t.includes("tag")) {
|
|
271
|
+
s += ` ${v}`;
|
|
272
|
+
if (!(d.name in this.extraArguments)) continue;
|
|
273
|
+
v = this.extraArguments[d.name];
|
|
274
|
+
t = d.extraArg.type;
|
|
275
|
+
}
|
|
276
|
+
s += " ";
|
|
277
|
+
if (Array.isArray(v)) {
|
|
278
|
+
s += d.type[0] === "testlist" ? `(${v.map((x) => x.toSieve(0).trim().replace(/;$/, "")).join(", ")})` : `[${v.map((x) => isQuoted(x) ? x : quote(x)).join(", ")}]`;
|
|
279
|
+
} else if (v instanceof _Command) s += v.toSieve(0).trim().replace(/;$/, "");
|
|
280
|
+
else s += t.includes("string") ? String(v) : String(v);
|
|
281
|
+
}
|
|
282
|
+
if (!this.acceptChildren) return this.type === "test" ? s : `${s};
|
|
283
|
+
`;
|
|
284
|
+
if (this.type !== "control") return s;
|
|
285
|
+
return `${s} {
|
|
286
|
+
${this.children.map((c) => c.toSieve(indent + 4)).join("")}${" ".repeat(indent)}}
|
|
287
|
+
`;
|
|
288
|
+
}
|
|
289
|
+
tosieve(indent = 0) {
|
|
290
|
+
return this.toSieve(indent);
|
|
291
|
+
}
|
|
292
|
+
};
|
|
293
|
+
var ControlCommand = class extends Command {
|
|
294
|
+
static type = "control";
|
|
295
|
+
};
|
|
296
|
+
var ActionCommand = class extends Command {
|
|
297
|
+
static type = "action";
|
|
298
|
+
};
|
|
299
|
+
var TestCommand = class extends Command {
|
|
300
|
+
static type = "test";
|
|
301
|
+
};
|
|
302
|
+
function concrete(name, base, defs = [], options = {}) {
|
|
303
|
+
const C = class extends base {
|
|
304
|
+
static argsDefinition = defs;
|
|
305
|
+
static acceptChildren = options.acceptChildren ?? false;
|
|
306
|
+
static variableArgs = options.variableArgs ?? false;
|
|
307
|
+
static nonDeterministic = options.nonDeterministic ?? false;
|
|
308
|
+
static extension = options.extension;
|
|
309
|
+
static mustFollow = options.mustFollow;
|
|
310
|
+
};
|
|
311
|
+
Object.defineProperty(C, "name", { value: `${name[0].toUpperCase()}${name.slice(1)}Command` });
|
|
312
|
+
return C;
|
|
313
|
+
}
|
|
314
|
+
var RequireCommand = concrete("require", ControlCommand, [{ name: "capabilities", type: ["string", "stringlist"], required: true }]);
|
|
315
|
+
var IfCommand = concrete("if", ControlCommand, [{ name: "test", type: ["test"], required: true }], { acceptChildren: true });
|
|
316
|
+
var ElsifCommand = concrete("elsif", ControlCommand, [{ name: "test", type: ["test"], required: true }], { acceptChildren: true, mustFollow: ["if", "elsif"] });
|
|
317
|
+
var ElseCommand = concrete("else", ControlCommand, [], { acceptChildren: true, mustFollow: ["if", "elsif"] });
|
|
318
|
+
var StopCommand = concrete("stop", ActionCommand);
|
|
319
|
+
var FileintoCommand = concrete("fileinto", ActionCommand, [{ name: "copy", type: ["tag"], values: [":copy"], extension: "copy" }, { name: "create", type: ["tag"], values: [":create"], extension: "mailbox" }, { name: "flags", type: ["tag"], values: [":flags"], extraArg: { type: ["string", "stringlist"] }, extension: "imap4flags" }, { name: "mailbox", type: ["string"], required: true }], { extension: "fileinto" });
|
|
320
|
+
var RedirectCommand = concrete("redirect", ActionCommand, [{ name: "copy", type: ["tag"], values: [":copy"], extension: "copy" }, { name: "address", type: ["string"], required: true }]);
|
|
321
|
+
var RejectCommand = concrete("reject", ActionCommand, [{ name: "text", type: ["string"], required: true }], { extension: "reject" });
|
|
322
|
+
var KeepCommand = concrete("keep", ActionCommand, [{ name: "flags", type: ["tag"], values: [":flags"], extraArg: { type: ["string", "stringlist"] }, extension: "imap4flags" }]);
|
|
323
|
+
var DiscardCommand = concrete("discard", ActionCommand);
|
|
324
|
+
var flagDefs = [{ name: "variable-name", type: ["string"] }, { name: "list-of-flags", type: ["string", "stringlist"], required: true }];
|
|
325
|
+
var SetflagCommand = concrete("setflag", ActionCommand, flagDefs, { extension: "imap4flags", nonDeterministic: true });
|
|
326
|
+
var AddflagCommand = concrete("addflag", ActionCommand, flagDefs, { extension: "imap4flags", nonDeterministic: true });
|
|
327
|
+
var RemoveflagCommand = concrete("removeflag", ActionCommand, flagDefs, { extension: "imap4flags", nonDeterministic: true });
|
|
328
|
+
var NotifyCommand = concrete("notify", ActionCommand, [{ name: "from", type: ["tag"], values: [":from"], extraArg: { type: ["string"] } }, { name: "importance", type: ["tag"], values: [":importance"], extraArg: { type: ["string"] } }, { name: "options", type: ["tag"], values: [":options"], extraArg: { type: ["stringlist"] } }, { name: "message", type: ["tag"], values: [":message"], extraArg: { type: ["string"] } }, { name: "method", type: ["string"], required: true }], { extension: "enotify" });
|
|
329
|
+
var addr = [comparator, addressPart, matchType, { name: "header-list", type: ["string", "stringlist"], required: true }, { name: "key-list", type: ["string", "stringlist"], required: true }];
|
|
330
|
+
var AddressCommand = concrete("address", TestCommand, addr);
|
|
331
|
+
var EnvelopeCommand = concrete("envelope", TestCommand, addr, { extension: "envelope" });
|
|
332
|
+
var HeaderCommand = concrete("header", TestCommand, [comparator, matchType, { name: "header-names", type: ["string", "stringlist"], required: true }, { name: "key-list", type: ["string", "stringlist"], required: true }]);
|
|
333
|
+
var ExistsCommand = concrete("exists", TestCommand, [{ name: "header-names", type: ["string", "stringlist"], required: true }]);
|
|
334
|
+
var TrueCommand = concrete("true", TestCommand);
|
|
335
|
+
var FalseCommand = concrete("false", TestCommand);
|
|
336
|
+
var AllofCommand = concrete("allof", TestCommand, [{ name: "tests", type: ["testlist"], required: true }], { acceptChildren: true, variableArgs: true });
|
|
337
|
+
var AnyofCommand = concrete("anyof", TestCommand, [{ name: "tests", type: ["testlist"], required: true }], { acceptChildren: true, variableArgs: true });
|
|
338
|
+
var NotCommand = concrete("not", TestCommand, [{ name: "test", type: ["test"], required: true }], { acceptChildren: true });
|
|
339
|
+
var SizeCommand = concrete("size", TestCommand, [{ name: "comparator", type: ["tag"], values: [":over", ":under"], required: true }, { name: "limit", type: ["number"], required: true }]);
|
|
340
|
+
var BodyCommand = concrete("body", TestCommand, [comparator, matchType, { name: "body-transform", type: ["tag"], values: [":raw", ":content", ":text"], extraArg: { type: ["stringlist"], validFor: [":content"] } }, { name: "key-list", type: ["string", "stringlist"], required: true }], { extension: "body" });
|
|
341
|
+
var HasflagCommand = concrete("hasflag", TestCommand, [comparator, matchType, { name: "variable-list", type: ["string", "stringlist"] }, { name: "list-of-flags", type: ["string", "stringlist"], required: true }], { extension: "imap4flags", nonDeterministic: true });
|
|
342
|
+
var DateCommand = concrete("date", TestCommand, [{ name: "zone", type: ["tag"], values: [":zone", ":originalzone"], extraArg: { type: ["string"], validFor: [":zone"] } }, comparator, matchType, { name: "header-name", type: ["string"], required: true }, { name: "date-part", type: ["string"], required: true }, { name: "key-list", type: ["string", "stringlist"], required: true }], { extension: "date" });
|
|
343
|
+
var CurrentdateCommand = concrete("currentdate", TestCommand, [{ name: "zone", type: ["tag"], values: [":zone"], extraArg: { type: ["string"] } }, comparator, matchType, { name: "date-part", type: ["string"], required: true }, { name: "key-list", type: ["string", "stringlist"], required: true }], { extension: "date" });
|
|
344
|
+
var EnvironmentCommand = concrete("environment", TestCommand, [comparator, matchType, { name: "name", type: ["string"], required: true }, { name: "key-list", type: ["string", "stringlist"], required: true }], { extension: "environment" });
|
|
345
|
+
var VacationCommand = concrete("vacation", ActionCommand, [{ name: "subject", type: ["tag"], values: [":subject"], extraArg: { type: ["string"] } }, { name: "days", type: ["tag"], values: [":days"], extraArg: { type: ["number"] } }, { name: "seconds", type: ["tag"], extensionValues: { ":seconds": "vacation-seconds" }, extraArg: { type: ["number"] } }, { name: "from", type: ["tag"], values: [":from"], extraArg: { type: ["string"] } }, { name: "addresses", type: ["tag"], values: [":addresses"], extraArg: { type: ["string", "stringlist"] } }, { name: "handle", type: ["tag"], values: [":handle"], extraArg: { type: ["string"] } }, { name: "mime", type: ["tag"], values: [":mime"] }, { name: "reason", type: ["string"], required: true }], { extension: "vacation" });
|
|
346
|
+
var SetCommand = concrete("set", ActionCommand, [{ name: "case", type: ["tag"], values: [":lower", ":upper"] }, { name: "first-case", type: ["tag"], values: [":lowerfirst", ":upperfirst"] }, { name: "quotewildcard", type: ["tag"], values: [":quotewildcard"] }, { name: "encoding", type: ["tag"], extensionValues: { ":quoteregex": "regex", ":encodeurl": "enotify" } }, { name: "length", type: ["tag"], values: [":length"] }, { name: "name", type: ["string"], required: true }, { name: "value", type: ["string"], required: true }], { extension: "variables" });
|
|
347
|
+
var registry = /* @__PURE__ */ new Map();
|
|
348
|
+
for (const x of [RequireCommand, IfCommand, ElsifCommand, ElseCommand, StopCommand, FileintoCommand, RedirectCommand, RejectCommand, KeepCommand, DiscardCommand, SetflagCommand, AddflagCommand, RemoveflagCommand, NotifyCommand, AddressCommand, EnvelopeCommand, HeaderCommand, ExistsCommand, TrueCommand, FalseCommand, AllofCommand, AnyofCommand, NotCommand, SizeCommand, BodyCommand, HasflagCommand, DateCommand, CurrentdateCommand, EnvironmentCommand, VacationCommand, SetCommand]) registry.set(x.name.replace(/Command$/, "").toLowerCase(), x);
|
|
349
|
+
function addCommands(cmds) {
|
|
350
|
+
for (const c of Array.isArray(cmds) ? cmds : [cmds]) registry.set(c.name.replace(/Command$/, "").toLowerCase(), c);
|
|
351
|
+
}
|
|
352
|
+
function getCommandInstance(name, parent, checkexists = true) {
|
|
353
|
+
const C = registry.get(name.toLowerCase());
|
|
354
|
+
if (!C) throw new UnknownCommand(name);
|
|
355
|
+
const c = new C(parent);
|
|
356
|
+
if (checkexists && c.extension && !loadedExtensions.includes(c.extension)) throw new ExtensionNotLoaded(c.extension);
|
|
357
|
+
return c;
|
|
358
|
+
}
|
|
359
|
+
var parser_state = {
|
|
360
|
+
get loaded_extensions() {
|
|
361
|
+
return loadedExtensions;
|
|
362
|
+
},
|
|
363
|
+
set loaded_extensions(value) {
|
|
364
|
+
loadedExtensions = value;
|
|
365
|
+
}
|
|
366
|
+
};
|
|
367
|
+
var add_commands = addCommands;
|
|
368
|
+
var get_command_instance = getCommandInstance;
|
|
369
|
+
// Annotate the CommonJS export names for ESM import in node:
|
|
370
|
+
0 && (module.exports = {
|
|
371
|
+
ActionCommand,
|
|
372
|
+
AddflagCommand,
|
|
373
|
+
AddressCommand,
|
|
374
|
+
AllofCommand,
|
|
375
|
+
AnyofCommand,
|
|
376
|
+
BadArgument,
|
|
377
|
+
BadValue,
|
|
378
|
+
BodyCommand,
|
|
379
|
+
Command,
|
|
380
|
+
CommandError,
|
|
381
|
+
ControlCommand,
|
|
382
|
+
CurrentdateCommand,
|
|
383
|
+
DateCommand,
|
|
384
|
+
DiscardCommand,
|
|
385
|
+
ElseCommand,
|
|
386
|
+
ElsifCommand,
|
|
387
|
+
EnvelopeCommand,
|
|
388
|
+
EnvironmentCommand,
|
|
389
|
+
ExistsCommand,
|
|
390
|
+
ExtensionNotLoaded,
|
|
391
|
+
FalseCommand,
|
|
392
|
+
FileintoCommand,
|
|
393
|
+
HasflagCommand,
|
|
394
|
+
HeaderCommand,
|
|
395
|
+
IfCommand,
|
|
396
|
+
KeepCommand,
|
|
397
|
+
NotCommand,
|
|
398
|
+
NotifyCommand,
|
|
399
|
+
RedirectCommand,
|
|
400
|
+
RejectCommand,
|
|
401
|
+
RemoveflagCommand,
|
|
402
|
+
RequireCommand,
|
|
403
|
+
SetCommand,
|
|
404
|
+
SetflagCommand,
|
|
405
|
+
SizeCommand,
|
|
406
|
+
StopCommand,
|
|
407
|
+
TestCommand,
|
|
408
|
+
TrueCommand,
|
|
409
|
+
UnknownCommand,
|
|
410
|
+
VacationCommand,
|
|
411
|
+
addCommands,
|
|
412
|
+
add_commands,
|
|
413
|
+
getCommandInstance,
|
|
414
|
+
get_command_instance,
|
|
415
|
+
loadedExtensions,
|
|
416
|
+
parser_state,
|
|
417
|
+
resetExtensions
|
|
418
|
+
});
|
|
419
|
+
//# sourceMappingURL=commands.cjs.map
|