1#![doc = include_str!("../README.md")]
8
9use ahash::{AHashMap, AHashSet};
10use compiler::grammar::Capability;
11use mail_parser::{HeaderName, Message};
12use runtime::{
13 RuntimeError, Variable,
14 context::{Frame, Pending},
15 handler::Action,
16};
17use std::{
18 borrow::Cow,
19 cell::{Cell, RefCell},
20};
21
22pub mod bytecode;
23pub mod compiler;
24mod regex;
25pub mod runtime;
26pub mod sieve;
27
28pub use runtime::{
29 Action as SieveAction, Arena, Handler, Input, Mailbox, MessageSource, Recipient, Reply, Script,
30 Status,
31};
32pub use sieve::{LoadError, ScriptArena, Sieve};
33
34pub(crate) const MAX_MATCH_VARIABLES: u32 = 63;
35
36#[derive(Clone)]
37pub struct Compiler {
38 pub(crate) max_script_size: usize,
39 pub(crate) max_string_size: usize,
40 pub(crate) max_variable_name_size: usize,
41 pub(crate) max_nested_blocks: usize,
42 pub(crate) max_nested_tests: usize,
43 pub(crate) max_nested_foreverypart: usize,
44 pub(crate) max_match_variables: usize,
45 pub(crate) max_local_variables: usize,
46 pub(crate) max_header_size: usize,
47 pub(crate) max_includes: usize,
48 pub(crate) no_capability_check: bool,
49 pub(crate) functions: AHashMap<String, (u32, u32)>,
50}
51
52pub type Function = for<'x> fn(&Context<'x>, &[Variable<'x>]) -> Variable<'x>;
53
54#[derive(Default, Clone)]
55pub struct FunctionMap {
56 pub(crate) map: AHashMap<String, (u32, u32)>,
57 pub(crate) functions: Vec<Function>,
58}
59
60#[derive(Debug, Clone)]
61pub struct Runtime {
62 pub(crate) allowed_capabilities: AHashSet<Capability>,
63 pub(crate) valid_notification_uris: AHashSet<Cow<'static, str>>,
64 pub(crate) valid_ext_lists: AHashSet<Cow<'static, str>>,
65 pub(crate) protected_headers: Vec<HeaderName<'static>>,
66 pub(crate) environment: AHashMap<Cow<'static, str>, Variable<'static>>,
67 pub(crate) metadata: Vec<(Metadata<String>, Cow<'static, str>)>,
68 pub(crate) include_scripts: AHashMap<String, Sieve<'static>>,
69 pub(crate) local_hostname: Cow<'static, str>,
70 pub(crate) functions: Vec<Function>,
71
72 pub(crate) max_nested_includes: usize,
73 pub(crate) cpu_limit: usize,
74 pub(crate) memory_limit: usize,
75 pub(crate) max_variable_size: usize,
76 pub(crate) max_redirects: usize,
77 pub(crate) max_received_headers: usize,
78 pub(crate) max_header_size: usize,
79 pub(crate) max_out_messages: usize,
80
81 pub(crate) default_vacation_expiry: u64,
82 pub(crate) default_duplicate_expiry: u64,
83
84 pub(crate) vacation_use_orig_rcpt: bool,
85 pub(crate) vacation_default_subject: Cow<'static, str>,
86 pub(crate) vacation_subject_prefix: Cow<'static, str>,
87}
88
89pub struct Context<'x> {
90 pub(crate) runtime: &'x Runtime,
91 pub(crate) user_address: Cow<'x, str>,
92 pub(crate) user_full_name: Cow<'x, str>,
93 pub(crate) current_time: i64,
94
95 pub(crate) message: Message<'x>,
96 pub(crate) message_size: usize,
97 pub(crate) envelope: Vec<(Envelope, Variable<'x>)>,
98 pub(crate) metadata: Vec<(Metadata<String>, Cow<'x, str>)>,
99
100 pub(crate) part: u32,
101 pub(crate) part_iter: Vec<u32>,
102 pub(crate) part_iter_pos: usize,
103 pub(crate) part_iter_stack: Vec<(u32, Vec<u32>, usize)>,
104
105 pub(crate) spam_status: SpamStatus,
106 pub(crate) virus_status: VirusStatus,
107
108 pub(crate) script: &'x Sieve<'x>,
109 pub(crate) frames: Vec<Frame<'x>>,
110 pub(crate) local_base: usize,
111 pub(crate) match_base: usize,
112 pub(crate) pos: usize,
113 pub(crate) test_result: bool,
114 pub(crate) pending: Pending<'x>,
115 pub(crate) error: Option<RuntimeError>,
116 pub(crate) rejected: bool,
117 pub(crate) included: Vec<Script<'x>>,
118 pub(crate) vars_global: AHashMap<Cow<'x, str>, Variable<'x>>,
119 pub(crate) vars_env: AHashMap<Cow<'static, str>, Variable<'x>>,
120 pub(crate) vars_local: Vec<Variable<'x>>,
121 pub(crate) vars_match: Vec<Variable<'x>>,
122 pub(crate) expr_stack: Vec<Variable<'x>>,
123 pub(crate) expr_pos: usize,
124
125 pub(crate) flags: Vec<&'x str>,
126 pub(crate) actions: Vec<Action<'x>>,
127 pub(crate) final_action: Option<Action<'x>>,
128 pub(crate) last_message_id: usize,
129 pub(crate) main_message_id: usize,
130
131 pub(crate) has_changes: bool,
132 pub(crate) oom: Cell<bool>,
133 pub(crate) raw_message_copy: Cell<Option<&'x [u8]>>,
134 pub(crate) dynamic_regexes: RefCell<AHashMap<&'x str, Option<fancy_regex::Regex>>>,
135 pub(crate) num_redirects: usize,
136 pub(crate) num_instructions: usize,
137 pub(crate) num_out_messages: usize,
138
139 pub(crate) arena: &'x mut Arena,
140}
141
142#[derive(Debug, Clone, Copy, Eq, PartialEq, Hash)]
143#[cfg_attr(
144 any(test, feature = "serde"),
145 derive(serde::Serialize, serde::Deserialize)
146)]
147#[repr(u8)]
148pub enum Envelope {
149 From = 0,
150 To = 1,
151 ByTimeAbsolute = 2,
152 ByTimeRelative = 3,
153 ByMode = 4,
154 ByTrace = 5,
155 Notify = 6,
156 Orcpt = 7,
157 Ret = 8,
158 Envid = 9,
159}
160
161impl Envelope {
162 #[inline(always)]
163 pub(crate) fn from_code(code: u8) -> Envelope {
164 match code {
165 0 => Envelope::From,
166 1 => Envelope::To,
167 2 => Envelope::ByTimeAbsolute,
168 3 => Envelope::ByTimeRelative,
169 4 => Envelope::ByMode,
170 5 => Envelope::ByTrace,
171 6 => Envelope::Notify,
172 7 => Envelope::Orcpt,
173 8 => Envelope::Ret,
174 _ => Envelope::Envid,
175 }
176 }
177}
178
179#[derive(Debug, Clone, Eq, PartialEq, Hash)]
180#[cfg_attr(
181 any(test, feature = "serde"),
182 derive(serde::Serialize, serde::Deserialize)
183)]
184#[repr(u8)]
185pub enum Metadata<T> {
186 Server { annotation: T } = 0,
187 Mailbox { name: T, annotation: T } = 1,
188}
189
190pub type ExternalId = u32;
191
192#[derive(Debug, Clone, PartialEq, Eq, Hash)]
193#[cfg_attr(
194 any(test, feature = "serde"),
195 derive(serde::Serialize, serde::Deserialize)
196)]
197pub(crate) struct FileCarbonCopy<T> {
198 pub mailbox: T,
199 pub mailbox_id: Option<T>,
200 pub create: bool,
201 pub flags: Box<[T]>,
202 pub special_use: Option<T>,
203}
204
205#[derive(Debug, Clone, Copy, Eq, PartialEq, Hash)]
206pub enum Importance {
207 High,
208 Normal,
209 Low,
210}
211
212#[derive(Debug, Clone, Copy, Eq, PartialEq)]
213pub enum MatchAs {
214 Octet,
215 Lowercase,
216 Number,
217}
218
219#[derive(Debug, Clone, Copy, PartialEq)]
220pub enum SpamStatus {
221 Unknown,
222 Ham,
223 MaybeSpam(f64),
224 Spam,
225}
226
227#[derive(Debug, Clone, Copy, Eq, PartialEq, Hash)]
228pub enum VirusStatus {
229 Unknown,
230 Clean,
231 Replaced,
232 Cured,
233 MaybeVirus,
234 Virus,
235}
236
237#[cfg(test)]
238mod tests;