Skip to main content

sieve/
lib.rs

1/*
2 * SPDX-FileCopyrightText: 2020 Stalwart Labs Ltd <hello@stalw.art>
3 *
4 * SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-SEL
5 */
6
7#![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;