1pub mod actions;
25pub mod context;
26pub mod eval;
27pub mod expression;
28pub mod serialize;
29pub mod tests;
30pub mod variables;
31
32use std::{borrow::Cow, fmt::Display, hash::Hash, ops::Deref, sync::Arc};
33
34use ahash::{AHashMap, AHashSet};
35#[cfg(not(test))]
36use mail_parser::{Encoding, Message, MessageParser, MessagePart, PartType};
37
38use mail_parser::HeaderName;
39use serde::{Deserialize, Serialize};
40
41#[cfg(not(test))]
42use crate::Context;
43
44use crate::{
45 compiler::{
46 grammar::{expr::parser::ID_EXTERNAL, Capability, Invalid},
47 Number,
48 },
49 ExternalId, Function, FunctionMap, Input, Metadata, Runtime, Script, Sieve,
50};
51
52use self::eval::ToString;
53
54#[derive(Debug, Clone, Serialize, Deserialize)]
55pub enum Variable {
56 String(Arc<String>),
57 Integer(i64),
58 Float(f64),
59 Array(Arc<Vec<Variable>>),
60}
61
62#[derive(Debug)]
63pub enum RuntimeError {
64 TooManyIncludes,
65 InvalidInstruction(Invalid),
66 ScriptErrorMessage(String),
67 CapabilityNotAllowed(Capability),
68 CapabilityNotSupported(String),
69 CPULimitReached,
70}
71
72impl Default for Variable {
73 fn default() -> Self {
74 Variable::String(Arc::new(String::new()))
75 }
76}
77
78impl Variable {
79 pub fn to_string(&self) -> Cow<'_, str> {
80 match self {
81 Variable::String(s) => Cow::Borrowed(s.as_str()),
82 Variable::Integer(n) => Cow::Owned(n.to_string()),
83 Variable::Float(n) => Cow::Owned(n.to_string()),
84 Variable::Array(l) => Cow::Owned(l.to_string()),
85 }
86 }
87
88 pub fn to_number(&self) -> Number {
89 self.to_number_checked()
90 .unwrap_or(Number::Float(f64::INFINITY))
91 }
92
93 pub fn to_number_checked(&self) -> Option<Number> {
94 let s = match self {
95 Variable::Integer(n) => return Number::Integer(*n).into(),
96 Variable::Float(n) => return Number::Float(*n).into(),
97 Variable::String(s) if !s.is_empty() => s.as_str(),
98 _ => return None,
99 };
100
101 if !s.contains('.') {
102 s.parse::<i64>().map(Number::Integer).ok()
103 } else {
104 s.parse::<f64>().map(Number::Float).ok()
105 }
106 }
107
108 pub fn to_integer(&self) -> i64 {
109 match self {
110 Variable::Integer(n) => *n,
111 Variable::Float(n) => *n as i64,
112 Variable::String(s) if !s.is_empty() => s.parse::<i64>().unwrap_or(0),
113 _ => 0,
114 }
115 }
116
117 pub fn to_usize(&self) -> usize {
118 match self {
119 Variable::Integer(n) => *n as usize,
120 Variable::Float(n) => *n as usize,
121 Variable::String(s) if !s.is_empty() => s.parse::<usize>().unwrap_or(0),
122 _ => 0,
123 }
124 }
125
126 pub fn len(&self) -> usize {
127 match self {
128 Variable::String(s) => s.len(),
129 Variable::Integer(_) | Variable::Float(_) => 2,
130 Variable::Array(l) => l.iter().map(|v| v.len() + 2).sum(),
131 }
132 }
133
134 pub fn is_empty(&self) -> bool {
135 match self {
136 Variable::String(s) => s.is_empty(),
137 _ => false,
138 }
139 }
140
141 pub fn as_array(&self) -> Option<&[Variable]> {
142 match self {
143 Variable::Array(l) => Some(l),
144 _ => None,
145 }
146 }
147
148 pub fn into_array(self) -> Arc<Vec<Variable>> {
149 match self {
150 Variable::Array(l) => l,
151 v if !v.is_empty() => vec![v].into(),
152 _ => vec![].into(),
153 }
154 }
155
156 pub fn to_array(&self) -> Arc<Vec<Variable>> {
157 match self {
158 Variable::Array(l) => l.clone(),
159 v if !v.is_empty() => vec![v.clone()].into(),
160 _ => vec![].into(),
161 }
162 }
163
164 pub fn into_string_array(self) -> Vec<String> {
165 match self {
166 Variable::Array(l) => l.iter().map(|i| i.to_string().into_owned()).collect(),
167 v if !v.is_empty() => vec![v.to_string().into_owned()],
168 _ => vec![],
169 }
170 }
171
172 pub fn to_string_array(&self) -> Vec<Cow<'_, str>> {
173 match self {
174 Variable::Array(l) => l.iter().map(|i| i.to_string()).collect(),
175 v if !v.is_empty() => vec![v.to_string()],
176 _ => vec![],
177 }
178 }
179}
180
181impl From<String> for Variable {
182 fn from(s: String) -> Self {
183 Variable::String(s.into())
184 }
185}
186
187impl<'x> From<&'x String> for Variable {
188 fn from(s: &'x String) -> Self {
189 Variable::String(s.as_str().to_string().into())
190 }
191}
192
193impl<'x> From<&'x str> for Variable {
194 fn from(s: &'x str) -> Self {
195 Variable::String(s.to_string().into())
196 }
197}
198
199impl<'x> From<Cow<'x, str>> for Variable {
200 fn from(s: Cow<'x, str>) -> Self {
201 match s {
202 Cow::Borrowed(s) => Variable::String(s.to_string().into()),
203 Cow::Owned(s) => Variable::String(s.into()),
204 }
205 }
206}
207
208impl From<Vec<Variable>> for Variable {
209 fn from(l: Vec<Variable>) -> Self {
210 Variable::Array(l.into())
211 }
212}
213
214impl From<Number> for Variable {
215 fn from(n: Number) -> Self {
216 match n {
217 Number::Integer(n) => Variable::Integer(n),
218 Number::Float(n) => Variable::Float(n),
219 }
220 }
221}
222
223impl From<usize> for Variable {
224 fn from(n: usize) -> Self {
225 Variable::Integer(n as i64)
226 }
227}
228
229impl From<i64> for Variable {
230 fn from(n: i64) -> Self {
231 Variable::Integer(n)
232 }
233}
234
235impl From<u64> for Variable {
236 fn from(n: u64) -> Self {
237 Variable::Integer(n as i64)
238 }
239}
240
241impl From<f64> for Variable {
242 fn from(n: f64) -> Self {
243 Variable::Float(n)
244 }
245}
246
247impl From<i32> for Variable {
248 fn from(n: i32) -> Self {
249 Variable::Integer(n as i64)
250 }
251}
252
253impl From<u32> for Variable {
254 fn from(n: u32) -> Self {
255 Variable::Integer(n as i64)
256 }
257}
258
259impl From<bool> for Variable {
260 fn from(b: bool) -> Self {
261 Variable::Integer(i64::from(b))
262 }
263}
264
265impl PartialEq for Number {
266 fn eq(&self, other: &Self) -> bool {
267 match (self, other) {
268 (Self::Integer(a), Self::Integer(b)) => a == b,
269 (Self::Float(a), Self::Float(b)) => a == b,
270 (Self::Integer(a), Self::Float(b)) => (*a as f64) == *b,
271 (Self::Float(a), Self::Integer(b)) => *a == (*b as f64),
272 }
273 }
274}
275
276impl Eq for Number {}
277
278impl PartialOrd for Number {
279 fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
280 let (a, b) = match (self, other) {
281 (Number::Integer(a), Number::Integer(b)) => return a.partial_cmp(b),
282 (Number::Float(a), Number::Float(b)) => (*a, *b),
283 (Number::Integer(a), Number::Float(b)) => (*a as f64, *b),
284 (Number::Float(a), Number::Integer(b)) => (*a, *b as f64),
285 };
286 a.partial_cmp(&b)
287 }
288}
289
290impl self::eval::ToString for Vec<Variable> {
291 fn to_string(&self) -> String {
292 let mut result = String::with_capacity(self.len() * 10);
293 for item in self {
294 if !result.is_empty() {
295 result.push_str("\r\n");
296 }
297 match item {
298 Variable::String(v) => result.push_str(v),
299 Variable::Integer(v) => result.push_str(&v.to_string()),
300 Variable::Float(v) => result.push_str(&v.to_string()),
301 Variable::Array(_) => {}
302 }
303 }
304 result
305 }
306}
307
308impl Hash for Variable {
309 fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
310 match self {
311 Variable::String(s) => s.hash(state),
312 Variable::Integer(n) => n.hash(state),
313 Variable::Float(n) => n.to_bits().hash(state),
314 Variable::Array(l) => l.hash(state),
315 }
316 }
317}
318
319#[cfg(not(test))]
320impl Runtime {
321 pub fn filter<'z: 'x, 'x>(&'z self, raw_message: &'x [u8]) -> Context<'x> {
322 Context::new(
323 self,
324 MessageParser::new()
325 .parse(raw_message)
326 .unwrap_or_else(|| Message {
327 parts: vec![MessagePart {
328 headers: vec![],
329 is_encoding_problem: false,
330 body: PartType::Text("".into()),
331 encoding: Encoding::None,
332 offset_header: 0,
333 offset_body: 0,
334 offset_end: 0,
335 }],
336 raw_message: b""[..].into(),
337 ..Default::default()
338 }),
339 )
340 }
341
342 pub fn filter_parsed<'z: 'x, 'x>(&'z self, message: Message<'x>) -> Context<'x> {
343 Context::new(self, message)
344 }
345}
346
347impl Default for Runtime {
348 fn default() -> Self {
349 Self::new()
350 }
351}
352
353impl Runtime {
354 pub fn new() -> Self {
355 #[allow(unused_mut)]
356 let mut allowed_capabilities = AHashSet::from_iter(Capability::all().iter().cloned());
357
358 #[cfg(test)]
359 allowed_capabilities.insert(Capability::Other("vnd.stalwart.testsuite".to_string()));
360
361 Runtime {
362 allowed_capabilities,
363 environment: AHashMap::from_iter([
364 ("name".into(), "Stalwart Sieve".into()),
365 ("version".into(), env!("CARGO_PKG_VERSION").into()),
366 ]),
367 metadata: Vec::new(),
368 include_scripts: AHashMap::new(),
369 max_nested_includes: 3,
370 cpu_limit: 5000,
371 max_variable_size: 4096,
372 max_redirects: 1,
373 max_received_headers: 10,
374 protected_headers: vec![
375 HeaderName::Other("Original-Subject".into()),
376 HeaderName::Other("Original-From".into()),
377 ],
378 valid_notification_uris: AHashSet::new(),
379 valid_ext_lists: AHashSet::new(),
380 vacation_use_orig_rcpt: false,
381 vacation_default_subject: "Automated reply".into(),
382 vacation_subject_prefix: "Auto: ".into(),
383 max_header_size: 1024,
384 max_out_messages: 3,
385 default_vacation_expiry: 30 * 86400,
386 default_duplicate_expiry: 7 * 86400,
387 local_hostname: "localhost".into(),
388 functions: Vec::new(),
389 }
390 }
391
392 pub fn set_cpu_limit(&mut self, size: usize) {
393 self.cpu_limit = size;
394 }
395
396 pub fn with_cpu_limit(mut self, size: usize) -> Self {
397 self.cpu_limit = size;
398 self
399 }
400
401 pub fn set_max_nested_includes(&mut self, size: usize) {
402 self.max_nested_includes = size;
403 }
404
405 pub fn with_max_nested_includes(mut self, size: usize) -> Self {
406 self.max_nested_includes = size;
407 self
408 }
409
410 pub fn set_max_redirects(&mut self, size: usize) {
411 self.max_redirects = size;
412 }
413
414 pub fn with_max_redirects(mut self, size: usize) -> Self {
415 self.max_redirects = size;
416 self
417 }
418
419 pub fn set_max_out_messages(&mut self, size: usize) {
420 self.max_out_messages = size;
421 }
422
423 pub fn with_max_out_messages(mut self, size: usize) -> Self {
424 self.max_out_messages = size;
425 self
426 }
427
428 pub fn set_max_received_headers(&mut self, size: usize) {
429 self.max_received_headers = size;
430 }
431
432 pub fn with_max_received_headers(mut self, size: usize) -> Self {
433 self.max_received_headers = size;
434 self
435 }
436
437 pub fn set_max_variable_size(&mut self, size: usize) {
438 self.max_variable_size = size;
439 }
440
441 pub fn with_max_variable_size(mut self, size: usize) -> Self {
442 self.max_variable_size = size;
443 self
444 }
445
446 pub fn set_max_header_size(&mut self, size: usize) {
447 self.max_header_size = size;
448 }
449
450 pub fn with_max_header_size(mut self, size: usize) -> Self {
451 self.max_header_size = size;
452 self
453 }
454
455 pub fn set_default_vacation_expiry(&mut self, expiry: u64) {
456 self.default_vacation_expiry = expiry;
457 }
458
459 pub fn with_default_vacation_expiry(mut self, expiry: u64) -> Self {
460 self.default_vacation_expiry = expiry;
461 self
462 }
463
464 pub fn set_default_duplicate_expiry(&mut self, expiry: u64) {
465 self.default_duplicate_expiry = expiry;
466 }
467
468 pub fn with_default_duplicate_expiry(mut self, expiry: u64) -> Self {
469 self.default_duplicate_expiry = expiry;
470 self
471 }
472
473 pub fn set_capability(&mut self, capability: impl Into<Capability>) {
474 self.allowed_capabilities.insert(capability.into());
475 }
476
477 pub fn with_capability(mut self, capability: impl Into<Capability>) -> Self {
478 self.set_capability(capability);
479 self
480 }
481
482 pub fn unset_capability(&mut self, capability: impl Into<Capability>) {
483 self.allowed_capabilities.remove(&capability.into());
484 }
485
486 pub fn without_capability(mut self, capability: impl Into<Capability>) -> Self {
487 self.unset_capability(capability);
488 self
489 }
490
491 pub fn without_capabilities(
492 mut self,
493 capabilities: impl IntoIterator<Item = impl Into<Capability>>,
494 ) -> Self {
495 for capability in capabilities {
496 self.allowed_capabilities.remove(&capability.into());
497 }
498 self
499 }
500
501 pub fn set_protected_header(&mut self, header_name: impl Into<Cow<'static, str>>) {
502 if let Some(header_name) = HeaderName::parse(header_name) {
503 self.protected_headers.push(header_name);
504 }
505 }
506
507 pub fn with_protected_header(mut self, header_name: impl Into<Cow<'static, str>>) -> Self {
508 self.set_protected_header(header_name);
509 self
510 }
511
512 pub fn with_protected_headers(
513 mut self,
514 header_names: impl IntoIterator<Item = impl Into<Cow<'static, str>>>,
515 ) -> Self {
516 self.protected_headers = header_names
517 .into_iter()
518 .filter_map(HeaderName::parse)
519 .collect();
520 self
521 }
522
523 pub fn set_env_variable(
524 &mut self,
525 name: impl Into<Cow<'static, str>>,
526 value: impl Into<Variable>,
527 ) {
528 self.environment.insert(name.into(), value.into());
529 }
530
531 pub fn with_env_variable(
532 mut self,
533 name: impl Into<Cow<'static, str>>,
534 value: impl Into<Cow<'static, str>>,
535 ) -> Self {
536 self.set_env_variable(name.into(), value.into());
537 self
538 }
539
540 pub fn set_medatata(
541 &mut self,
542 name: impl Into<Metadata<String>>,
543 value: impl Into<Cow<'static, str>>,
544 ) {
545 self.metadata.push((name.into(), value.into()));
546 }
547
548 pub fn with_metadata(
549 mut self,
550 name: impl Into<Metadata<String>>,
551 value: impl Into<Cow<'static, str>>,
552 ) -> Self {
553 self.set_medatata(name, value);
554 self
555 }
556
557 pub fn set_valid_notification_uri(&mut self, uri: impl Into<Cow<'static, str>>) {
558 self.valid_notification_uris.insert(uri.into());
559 }
560
561 pub fn with_valid_notification_uri(mut self, uri: impl Into<Cow<'static, str>>) -> Self {
562 self.valid_notification_uris.insert(uri.into());
563 self
564 }
565
566 pub fn with_valid_notification_uris(
567 mut self,
568 uris: impl IntoIterator<Item = impl Into<Cow<'static, str>>>,
569 ) -> Self {
570 self.valid_notification_uris = uris.into_iter().map(Into::into).collect();
571 self
572 }
573
574 pub fn set_valid_ext_list(&mut self, name: impl Into<Cow<'static, str>>) {
575 self.valid_ext_lists.insert(name.into());
576 }
577
578 pub fn with_valid_ext_list(mut self, name: impl Into<Cow<'static, str>>) -> Self {
579 self.set_valid_ext_list(name);
580 self
581 }
582
583 pub fn set_vacation_use_orig_rcpt(&mut self, value: bool) {
584 self.vacation_use_orig_rcpt = value;
585 }
586
587 pub fn with_valid_ext_lists(
588 mut self,
589 lists: impl IntoIterator<Item = impl Into<Cow<'static, str>>>,
590 ) -> Self {
591 self.valid_ext_lists = lists.into_iter().map(Into::into).collect();
592 self
593 }
594
595 pub fn with_vacation_use_orig_rcpt(mut self, value: bool) -> Self {
596 self.set_vacation_use_orig_rcpt(value);
597 self
598 }
599
600 pub fn set_vacation_default_subject(&mut self, value: impl Into<Cow<'static, str>>) {
601 self.vacation_default_subject = value.into();
602 }
603
604 pub fn with_vacation_default_subject(mut self, value: impl Into<Cow<'static, str>>) -> Self {
605 self.set_vacation_default_subject(value);
606 self
607 }
608
609 pub fn set_vacation_subject_prefix(&mut self, value: impl Into<Cow<'static, str>>) {
610 self.vacation_subject_prefix = value.into();
611 }
612
613 pub fn with_vacation_subject_prefix(mut self, value: impl Into<Cow<'static, str>>) -> Self {
614 self.set_vacation_subject_prefix(value);
615 self
616 }
617
618 pub fn set_local_hostname(&mut self, value: impl Into<Cow<'static, str>>) {
619 self.local_hostname = value.into();
620 }
621
622 pub fn with_local_hostname(mut self, value: impl Into<Cow<'static, str>>) -> Self {
623 self.set_local_hostname(value);
624 self
625 }
626
627 pub fn with_functions(mut self, fnc_map: &mut FunctionMap) -> Self {
628 self.functions = std::mem::take(&mut fnc_map.functions);
629 self
630 }
631
632 pub fn set_functions(&mut self, fnc_map: &mut FunctionMap) {
633 self.functions = std::mem::take(&mut fnc_map.functions);
634 }
635}
636
637impl FunctionMap {
638 pub fn new() -> Self {
639 FunctionMap {
640 map: Default::default(),
641 functions: Default::default(),
642 }
643 }
644
645 pub fn with_function(self, name: impl Into<String>, fnc: Function) -> Self {
646 self.with_function_args(name, fnc, 1)
647 }
648
649 pub fn with_function_no_args(self, name: impl Into<String>, fnc: Function) -> Self {
650 self.with_function_args(name, fnc, 0)
651 }
652
653 pub fn with_function_args(
654 mut self,
655 name: impl Into<String>,
656 fnc: Function,
657 num_args: u32,
658 ) -> Self {
659 self.map
660 .insert(name.into(), (self.functions.len() as u32, num_args));
661 self.functions.push(fnc);
662 self
663 }
664
665 pub fn with_external_function(
666 mut self,
667 name: impl Into<String>,
668 id: ExternalId,
669 num_args: u32,
670 ) -> Self {
671 self.set_external_function(name, id, num_args);
672 self
673 }
674
675 pub fn set_external_function(
676 &mut self,
677 name: impl Into<String>,
678 id: ExternalId,
679 num_args: u32,
680 ) {
681 self.map.insert(name.into(), (ID_EXTERNAL - id, num_args));
682 }
683}
684
685impl Input {
686 pub fn script(name: impl Into<Script>, script: impl Into<Arc<Sieve>>) -> Self {
687 Input::Script {
688 name: name.into(),
689 script: script.into(),
690 }
691 }
692
693 pub fn success() -> Self {
694 Input::True
695 }
696
697 pub fn fail() -> Self {
698 Input::False
699 }
700
701 pub fn result(result: Variable) -> Self {
702 Input::FncResult(result)
703 }
704}
705
706impl From<bool> for Input {
707 fn from(value: bool) -> Self {
708 if value {
709 Input::True
710 } else {
711 Input::False
712 }
713 }
714}
715
716impl From<Variable> for Input {
717 fn from(value: Variable) -> Self {
718 Input::FncResult(value)
719 }
720}
721
722impl Deref for Script {
723 type Target = String;
724
725 fn deref(&self) -> &Self::Target {
726 match self {
727 Script::Personal(name) | Script::Global(name) => name,
728 }
729 }
730}
731
732impl AsRef<str> for Script {
733 fn as_ref(&self) -> &str {
734 match self {
735 Script::Personal(name) | Script::Global(name) => name.as_str(),
736 }
737 }
738}
739
740impl AsRef<String> for Script {
741 fn as_ref(&self) -> &String {
742 match self {
743 Script::Personal(name) | Script::Global(name) => name,
744 }
745 }
746}
747
748impl Script {
749 pub fn into_string(self) -> String {
750 match self {
751 Script::Personal(name) | Script::Global(name) => name,
752 }
753 }
754
755 pub fn as_str(&self) -> &String {
756 match self {
757 Script::Personal(name) | Script::Global(name) => name,
758 }
759 }
760}
761
762impl Display for Script {
763 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
764 f.write_str(self.as_str())
765 }
766}
767
768impl From<String> for Script {
769 fn from(name: String) -> Self {
770 Script::Personal(name)
771 }
772}
773
774impl From<&str> for Script {
775 fn from(name: &str) -> Self {
776 Script::Personal(name.to_string())
777 }
778}
779
780impl<T> Metadata<T> {
781 pub fn server(annotation: impl Into<T>) -> Self {
782 Metadata::Server {
783 annotation: annotation.into(),
784 }
785 }
786
787 pub fn mailbox(name: impl Into<T>, annotation: impl Into<T>) -> Self {
788 Metadata::Mailbox {
789 name: name.into(),
790 annotation: annotation.into(),
791 }
792 }
793}
794
795impl From<String> for Metadata<String> {
796 fn from(annotation: String) -> Self {
797 Metadata::Server { annotation }
798 }
799}
800
801impl From<&'_ str> for Metadata<String> {
802 fn from(annotation: &'_ str) -> Self {
803 Metadata::Server {
804 annotation: annotation.to_string(),
805 }
806 }
807}
808
809impl From<(String, String)> for Metadata<String> {
810 fn from((name, annotation): (String, String)) -> Self {
811 Metadata::Mailbox { name, annotation }
812 }
813}
814
815impl From<(&'_ str, &'_ str)> for Metadata<String> {
816 fn from((name, annotation): (&'_ str, &'_ str)) -> Self {
817 Metadata::Mailbox {
818 name: name.to_string(),
819 annotation: annotation.to_string(),
820 }
821 }
822}