1use super::Arena;
8use super::{
9 RuntimeError, Variable,
10 handler::{Action, Handler, Input, Reply, Script, Status},
11 tests::{TestResult, test_envelope::parse_envelope_address},
12 variable::Array,
13};
14use crate::{
15 Context, Envelope, Metadata, Runtime, Sieve, SpamStatus, VirusStatus,
16 bytecode::{
17 cursor::Cursor,
18 ops,
19 rec::{Rec, tag},
20 },
21 compiler::grammar::Capability,
22};
23use ahash::AHashMap;
24use mail_parser::Message;
25use std::{
26 borrow::Cow,
27 cell::{Cell, RefCell},
28 time::SystemTime,
29};
30
31#[derive(Clone, Copy)]
32pub(crate) struct Frame<'x> {
33 pub(crate) script: &'x Sieve<'x>,
34 pub(crate) prev_pos: usize,
35 pub(crate) local_base: usize,
36 pub(crate) match_base: usize,
37}
38
39#[derive(Debug, Clone, Copy, PartialEq, Eq)]
40pub(crate) enum Pending<'x> {
41 Idle,
42 Test { is_not: bool },
43 Function,
44 Include { name: Script<'x>, optional: bool },
45 Action,
46 Finished,
47 Aborted,
48}
49
50impl<'x> Context<'x> {
51 pub fn new(
52 runtime: &'x Runtime,
53 message: Message<'x>,
54 script: &'x Sieve<'x>,
55 arena: &'x mut Arena,
56 ) -> Self {
57 arena.prepare(runtime.memory_limit);
58 let message_size = message.raw_message.len();
59 Context {
60 runtime,
61 message,
62 script,
63 frames: Vec::with_capacity(2),
64 local_base: 0,
65 match_base: 0,
66 part: 0,
67 part_iter: Vec::new(),
68 part_iter_pos: 0,
69 part_iter_stack: Vec::new(),
70 pos: 0,
71 test_result: false,
72 pending: Pending::Idle,
73 error: None,
74 rejected: false,
75 included: Vec::new(),
76 vars_global: AHashMap::new(),
77 vars_env: AHashMap::new(),
78 vars_local: Vec::with_capacity(script.num_vars()),
79 vars_match: Vec::with_capacity(script.num_match_vars()),
80 expr_stack: Vec::with_capacity(16),
81 expr_pos: 0,
82 envelope: Vec::new(),
83 metadata: Vec::new(),
84 message_size,
85 final_action: Some(Action::Keep {
86 flags: &[],
87 message_id: 0,
88 }),
89 flags: Vec::new(),
90 actions: Vec::new(),
91 has_changes: false,
92 oom: Cell::new(false),
93 raw_message_copy: Cell::new(None),
94 dynamic_regexes: RefCell::new(AHashMap::new()),
95 user_address: "".into(),
96 user_full_name: "".into(),
97 current_time: SystemTime::now()
98 .duration_since(SystemTime::UNIX_EPOCH)
99 .map(|d| d.as_secs())
100 .unwrap_or(0) as i64,
101 num_redirects: 0,
102 num_instructions: 0,
103 num_out_messages: 0,
104 last_message_id: 0,
105 main_message_id: 0,
106 virus_status: VirusStatus::Unknown,
107 spam_status: SpamStatus::Unknown,
108 arena,
109 }
110 }
111
112 #[inline(always)]
113 pub(crate) fn alloc_str(&self, s: &str) -> &'x str {
114 if s.is_empty() {
115 return "";
116 }
117 match self.arena.bump.try_alloc_slice_copy(s.as_bytes()) {
118 Ok(bytes) => unsafe { std::str::from_utf8_unchecked(extend(bytes)) },
119 Err(_) => {
120 self.note_oom();
121 ""
122 }
123 }
124 }
125
126 #[inline(always)]
127 pub(crate) fn alloc_string(&self, s: String) -> &'x str {
128 self.alloc_str(&s)
129 }
130
131 pub(crate) fn alloc_strs(&self, items: &[&'x str]) -> &'x [&'x str] {
132 if items.is_empty() {
133 return &[];
134 }
135 match self.arena.bump.try_alloc_slice_copy(items) {
136 Ok(slice) => unsafe { extend(slice) },
137 Err(_) => {
138 self.note_oom();
139 &[]
140 }
141 }
142 }
143
144 pub(crate) fn alloc_variables(&self, items: Vec<Variable<'x>>) -> &'x [Variable<'x>] {
145 match self.try_alloc_variables(items) {
146 Ok(slice) => slice,
147 Err(_) => {
148 self.note_oom();
149 &[]
150 }
151 }
152 }
153
154 pub(crate) fn try_alloc_variables<I>(
155 &self,
156 items: I,
157 ) -> Result<&'x [Variable<'x>], RuntimeError>
158 where
159 I: IntoIterator<Item = Variable<'x>>,
160 I::IntoIter: ExactSizeIterator,
161 {
162 let items = items.into_iter();
163 if items.len() == 0 {
164 return Ok(&[]);
165 }
166 self.arena
167 .bump
168 .try_alloc_slice_fill_iter(items.map(|v| self.intern(v)))
169 .map(|slice| unsafe { extend(slice) })
170 .map_err(|_| RuntimeError::MemoryLimitReached)
171 }
172
173 #[inline(always)]
174 pub(crate) fn intern(&self, value: Variable<'x>) -> Variable<'x> {
175 match value {
176 Variable::String(Cow::Owned(s)) => Variable::String(Cow::Borrowed(self.alloc_str(&s))),
177 Variable::Array(Array::Owned(items)) => {
178 Variable::Array(Array::Borrowed(self.alloc_variables(items)))
179 }
180 value => value,
181 }
182 }
183
184 #[inline(always)]
185 pub(crate) fn intern_cow(&self, value: Cow<'x, str>) -> &'x str {
186 match value {
187 Cow::Borrowed(s) => s,
188 Cow::Owned(s) => self.alloc_str(&s),
189 }
190 }
191
192 #[cold]
193 pub(crate) fn note_oom(&self) {
194 self.oom.set(true);
195 }
196
197 pub fn run<H: Handler<'x>>(&mut self, handler: &mut H) -> Result<Status, RuntimeError> {
198 match self.pending {
199 Pending::Idle => {
200 self.pending = Pending::Action;
201 self.push_frame(self.script);
202 }
203 Pending::Finished => return Ok(Status::Finished),
204 Pending::Aborted => {
205 return match self.error.take() {
206 Some(err) => Err(err),
207 None => self.finish(handler),
208 };
209 }
210 Pending::Action => (),
211 _ => return Err(RuntimeError::AwaitingInput),
212 }
213
214 match self.flush_actions(handler) {
215 Ok(true) => (),
216 Ok(false) => return Ok(Status::Pending),
217 Err(err) => return Err(self.abort(err)),
218 }
219
220 if self.frames.is_empty() {
221 return self.finish(handler);
222 }
223
224 let cpu_limit = self.runtime.cpu_limit;
225
226 'outer: loop {
227 if self.frames.is_empty() {
228 return self.finish(handler);
229 }
230 let script = self.script;
231 let mut cur = Cursor::new(script.code(), self.pos);
232
233 while !cur.at_end() {
234 self.num_instructions += 1;
235 if self.num_instructions > cpu_limit {
236 return Err(self.abort(RuntimeError::CPULimitReached));
237 }
238 if self.oom.get() {
239 return Err(self.abort(RuntimeError::MemoryLimitReached));
240 }
241 let start = cur.pos;
242 let op = cur.u8()?;
243
244 match op {
245 ops::Jz::OP => {
246 let jump = ops::Jz::decode(&mut cur)?;
247 if !self.test_result {
248 cur.pos = jump.target.0 as usize;
249 }
250 continue;
251 }
252 ops::Jnz::OP => {
253 let jump = ops::Jnz::decode(&mut cur)?;
254 if self.test_result {
255 cur.pos = jump.target.0 as usize;
256 }
257 continue;
258 }
259 ops::Jmp::OP => {
260 let jump = ops::Jmp::decode(&mut cur)?;
261 cur.pos = jump.target.0 as usize;
262 continue;
263 }
264 ops::Clear::OP => {
265 let clear = ops::Clear::decode(&mut cur)?;
266 self.exec_clear(&clear);
267 continue;
268 }
269 ops::Discard::OP => {
270 self.final_action = Some(Action::Discard);
271 continue;
272 }
273 ops::Stop::OP => {
274 self.pos = cur.pos;
275 self.frames.clear();
276 return self.finish(handler);
277 }
278 ops::Return::OP => {
279 self.pos = cur.pos;
280 self.pop_frame();
281 continue 'outer;
282 }
283 _ => (),
284 }
285
286 match self.exec_op(op, start, &mut cur, handler) {
287 Ok(Flow::Continue) => (),
288 Ok(Flow::Jump(target)) => {
289 cur.pos = target;
290 }
291 Ok(Flow::Switch) => {
292 continue 'outer;
293 }
294 Ok(Flow::Pending) => {
295 return Ok(Status::Pending);
296 }
297 Err(err) => {
298 return Err(self.abort(err));
299 }
300 }
301 }
302
303 self.pos = cur.pos;
304 self.pop_frame();
305 }
306 }
307
308 #[inline(always)]
309 fn exec_op<H: Handler<'x>>(
310 &mut self,
311 op: u8,
312 start: usize,
313 cur: &mut Cursor<'x>,
314 handler: &mut H,
315 ) -> Result<Flow, RuntimeError> {
316 let script = self.script;
317 let flow = match op {
318 ops::TestHeader::OP => {
319 let test = ops::TestHeader::decode(cur)?;
320 self.pos = cur.pos;
321 {
322 let result = self.test_header(script, &test, handler)?;
323 self.test_outcome(result)
324 }
325 }
326 ops::TestString::OP => {
327 let test = ops::TestString::decode(cur)?;
328 self.pos = cur.pos;
329 {
330 let result = self.test_string(script, &test, false, handler)?;
331 self.test_outcome(result)
332 }
333 }
334 ops::TestEnvironment::OP => {
335 let test = ops::TestEnvironment::decode(cur)?;
336 self.pos = cur.pos;
337 {
338 let result = self.test_environment(script, &test, handler)?;
339 self.test_outcome(result)
340 }
341 }
342 ops::TestAddress::OP => {
343 let test = ops::TestAddress::decode(cur)?;
344 self.pos = cur.pos;
345 {
346 let result = self.test_address(script, &test, handler)?;
347 self.test_outcome(result)
348 }
349 }
350 ops::TestEnvelope::OP => {
351 let test = ops::TestEnvelope::decode(cur)?;
352 self.pos = cur.pos;
353 {
354 let result = self.test_envelope(script, &test, handler)?;
355 self.test_outcome(result)
356 }
357 }
358 ops::TestExists::OP => {
359 let test = ops::TestExists::decode(cur)?;
360 self.pos = cur.pos;
361 {
362 let result = self.test_exists(script, &test)?;
363 self.test_outcome(result)
364 }
365 }
366 ops::TestSize::OP => {
367 let test = ops::TestSize::decode(cur)?;
368 self.pos = cur.pos;
369 self.test_outcome(self.test_size(&test))
370 }
371 ops::TestBody::OP => {
372 let test = ops::TestBody::decode(cur)?;
373 self.pos = cur.pos;
374 {
375 let result = self.test_body(script, &test)?;
376 self.test_outcome(result)
377 }
378 }
379 ops::TestDate::OP => {
380 let test = ops::TestDate::decode(cur)?;
381 self.pos = cur.pos;
382 {
383 let result = self.test_date(script, &test, handler)?;
384 self.test_outcome(result)
385 }
386 }
387 ops::TestCurrentDate::OP => {
388 let test = ops::TestCurrentDate::decode(cur)?;
389 self.pos = cur.pos;
390 {
391 let result = self.test_current_date(script, &test, handler)?;
392 self.test_outcome(result)
393 }
394 }
395 ops::TestDuplicate::OP => {
396 let test = ops::TestDuplicate::decode(cur)?;
397 self.pos = cur.pos;
398 {
399 let result = self.test_duplicate(script, &test, handler)?;
400 self.test_outcome(result)
401 }
402 }
403 ops::TestHasFlag::OP => {
404 let test = ops::TestHasFlag::decode(cur)?;
405 self.pos = cur.pos;
406 {
407 let result = self.test_hasflag(script, &test)?;
408 self.test_outcome(result)
409 }
410 }
411 ops::TestNotifyMethodCapability::OP => {
412 let test = ops::TestNotifyMethodCapability::decode(cur)?;
413 self.pos = cur.pos;
414 {
415 let result = self.test_notify_method_capability(script, &test)?;
416 self.test_outcome(result)
417 }
418 }
419 ops::TestValidNotifyMethod::OP => {
420 let test = ops::TestValidNotifyMethod::decode(cur)?;
421 self.pos = cur.pos;
422 {
423 let result = self.test_valid_notify_method(script, &test)?;
424 self.test_outcome(result)
425 }
426 }
427 ops::TestValidExtList::OP => {
428 let test = ops::TestValidExtList::decode(cur)?;
429 self.pos = cur.pos;
430 {
431 let result = self.test_valid_ext_list(script, &test)?;
432 self.test_outcome(result)
433 }
434 }
435 ops::TestIhave::OP => {
436 let test = ops::TestIhave::decode(cur)?;
437 self.pos = cur.pos;
438 {
439 let result = self.test_ihave(script, &test)?;
440 self.test_outcome(result)
441 }
442 }
443 ops::TestMailboxExists::OP => {
444 let test = ops::TestMailboxExists::decode(cur)?;
445 self.pos = cur.pos;
446 {
447 let result = self.test_mailbox_exists(script, &test, handler)?;
448 self.test_outcome(result)
449 }
450 }
451 ops::TestMailboxIdExists::OP => {
452 let test = ops::TestMailboxIdExists::decode(cur)?;
453 self.pos = cur.pos;
454 {
455 let result = self.test_mailbox_id_exists(script, &test, handler)?;
456 self.test_outcome(result)
457 }
458 }
459 ops::TestSpecialUseExists::OP => {
460 let test = ops::TestSpecialUseExists::decode(cur)?;
461 self.pos = cur.pos;
462 {
463 let result = self.test_special_use_exists(script, &test, handler)?;
464 self.test_outcome(result)
465 }
466 }
467 ops::TestMetadata::OP => {
468 let test = ops::TestMetadata::decode(cur)?;
469 self.pos = cur.pos;
470 {
471 let result = self.test_metadata(script, &test)?;
472 self.test_outcome(result)
473 }
474 }
475 ops::TestMetadataExists::OP => {
476 let test = ops::TestMetadataExists::decode(cur)?;
477 self.pos = cur.pos;
478 {
479 let result = self.test_metadata_exists(script, &test)?;
480 self.test_outcome(result)
481 }
482 }
483 ops::TestSpamTest::OP => {
484 let test = ops::TestSpamTest::decode(cur)?;
485 self.pos = cur.pos;
486 {
487 let result = self.test_spamtest(script, &test)?;
488 self.test_outcome(result)
489 }
490 }
491 ops::TestVirusTest::OP => {
492 let test = ops::TestVirusTest::decode(cur)?;
493 self.pos = cur.pos;
494 {
495 let result = self.test_virustest(script, &test)?;
496 self.test_outcome(result)
497 }
498 }
499 ops::TestVacation::OP => {
500 let test = ops::TestVacation::decode(cur)?;
501 self.pos = cur.pos;
502 {
503 let result = self.test_vacation(script, &test, handler)?;
504 self.test_outcome(result)
505 }
506 }
507 ops::TestConvert::OP => {
508 let test = ops::TestConvert::decode(cur)?;
509 self.pos = cur.pos;
510 {
511 let result = self.exec_convert(script, &test.into())?;
512 self.test_outcome(result)
513 }
514 }
515 ops::TestTrue::OP => {
516 self.pos = cur.pos;
517 self.test_result = true;
518 Flow::Continue
519 }
520 ops::TestFalse::OP => {
521 self.pos = cur.pos;
522 self.test_result = false;
523 Flow::Continue
524 }
525 ops::TestInvalid::OP => {
526 let test = ops::TestInvalid::decode(cur)?;
527 return Err(RuntimeError::InvalidInstruction {
528 name: script.str(test.name)?.to_string(),
529 line_num: test.line_num,
530 line_pos: test.line_pos,
531 });
532 }
533 ops::Eval::OP => {
534 let eval = ops::Eval::decode(cur)?;
535 self.pos = cur.pos;
536 match self.eval_expression(script, eval.expr, handler)? {
537 Some(result) => {
538 self.test_result = result.to_bool();
539 Flow::Continue
540 }
541 None => {
542 self.pos = start;
543 Flow::Pending
544 }
545 }
546 }
547 ops::Let::OP => {
548 let let_ = ops::Let::decode(cur)?;
549 self.pos = cur.pos;
550 match self.eval_expression(script, let_.expr, handler)? {
551 Some(result) => {
552 self.set_variable(script, let_.name, result)?;
553 Flow::Continue
554 }
555 None => {
556 self.pos = start;
557 Flow::Pending
558 }
559 }
560 }
561 ops::While::OP => {
562 let while_ = ops::While::decode(cur)?;
563 self.pos = cur.pos;
564 match self.eval_expression(script, while_.expr, handler)? {
565 Some(result) => {
566 if result.to_bool() {
567 Flow::Continue
568 } else {
569 Flow::Jump(while_.jz_pos.0 as usize)
570 }
571 }
572 None => {
573 self.pos = start;
574 Flow::Pending
575 }
576 }
577 }
578 ops::Set::OP => {
579 let set = ops::Set::decode(cur)?;
580 self.pos = cur.pos;
581 self.exec_set(script, &set)?;
582 self.flush(handler)?
583 }
584 ops::Keep::OP => {
585 let keep = ops::Keep::decode(cur)?;
586 self.pos = cur.pos;
587 self.exec_keep(script, &keep)?;
588 self.flush(handler)?
589 }
590 ops::FileInto::OP => {
591 let fileinto = ops::FileInto::decode(cur)?;
592 self.pos = cur.pos;
593 self.exec_fileinto(script, &fileinto)?;
594 self.flush(handler)?
595 }
596 ops::Redirect::OP => {
597 let redirect = ops::Redirect::decode(cur)?;
598 self.pos = cur.pos;
599 self.exec_redirect(script, &redirect)?;
600 self.flush(handler)?
601 }
602 ops::Reject::OP => {
603 let reject = ops::Reject::decode(cur)?;
604 self.pos = cur.pos;
605 let reason = self.eval_value(script, reject.reason)?.into_string();
606 self.final_action = None;
607 self.rejected = true;
608 let reason = self.intern_cow(reason);
609 self.actions.push(Action::Reject {
610 extended: reject.ereject,
611 reason,
612 });
613 self.flush(handler)?
614 }
615 ops::ForEveryPart::OP => {
616 let fep = ops::ForEveryPart::decode(cur)?;
617 self.pos = cur.pos;
618 if let Some(next_part) = self.next_part() {
619 self.part = next_part;
620 Flow::Continue
621 } else if let Some((prev_part, prev_iter, prev_pos)) = self.part_iter_stack.pop() {
622 self.part_iter = prev_iter;
623 self.part_iter_pos = prev_pos;
624 self.part = prev_part;
625 Flow::Jump(fep.jz_pos.0 as usize)
626 } else {
627 self.part = 0;
628 Flow::Continue
629 }
630 }
631 ops::ForEveryPartPush::OP => {
632 self.pos = cur.pos;
633 let part_iter = self.find_nested_parts_ids(self.part_iter_stack.is_empty());
634 let prev_iter = std::mem::replace(&mut self.part_iter, part_iter);
635 self.part_iter_stack
636 .push((self.part, prev_iter, self.part_iter_pos));
637 self.part_iter_pos = 0;
638 Flow::Continue
639 }
640 ops::ForEveryPartPop::OP => {
641 let pop = ops::ForEveryPartPop::decode(cur)?;
642 self.pos = cur.pos;
643 debug_assert!(
644 pop.num_pops > 0 && pop.num_pops as usize <= self.part_iter_stack.len(),
645 "Pop out of range: {} with {} items.",
646 pop.num_pops,
647 self.part_iter_stack.len()
648 );
649 for _ in 0..pop.num_pops {
650 if let Some((prev_part, prev_iter, prev_pos)) = self.part_iter_stack.pop() {
651 self.part_iter = prev_iter;
652 self.part_iter_pos = prev_pos;
653 self.part = prev_part;
654 } else {
655 break;
656 }
657 }
658 Flow::Continue
659 }
660 ops::Replace::OP => {
661 let replace = ops::Replace::decode(cur)?;
662 self.pos = cur.pos;
663 self.exec_replace(script, &replace)?;
664 Flow::Continue
665 }
666 ops::Enclose::OP => {
667 let enclose = ops::Enclose::decode(cur)?;
668 self.pos = cur.pos;
669 self.exec_enclose(script, &enclose)?;
670 Flow::Continue
671 }
672 ops::ExtractText::OP => {
673 let extract = ops::ExtractText::decode(cur)?;
674 self.pos = cur.pos;
675 self.exec_extracttext(script, &extract)?;
676 self.flush(handler)?
677 }
678 ops::AddHeader::OP => {
679 let add = ops::AddHeader::decode(cur)?;
680 self.pos = cur.pos;
681 self.exec_addheader(script, &add)?;
682 Flow::Continue
683 }
684 ops::DeleteHeader::OP => {
685 let delete = ops::DeleteHeader::decode(cur)?;
686 self.pos = cur.pos;
687 self.exec_deleteheader(script, &delete)?;
688 Flow::Continue
689 }
690 ops::Notify::OP => {
691 let notify = ops::Notify::decode(cur)?;
692 self.pos = cur.pos;
693 self.exec_notify(script, ¬ify)?;
694 self.flush(handler)?
695 }
696 ops::Vacation::OP => {
697 let vacation = ops::Vacation::decode(cur)?;
698 self.pos = cur.pos;
699 self.exec_vacation(script, &vacation)?;
700 self.flush(handler)?
701 }
702 ops::EditFlags::OP => {
703 let flags = ops::EditFlags::decode(cur)?;
704 self.pos = cur.pos;
705 self.exec_editflags(script, &flags)?;
706 Flow::Continue
707 }
708 ops::Convert::OP => {
709 let convert = ops::Convert::decode(cur)?;
710 self.pos = cur.pos;
711 self.exec_convert(script, &convert)?;
712 Flow::Continue
713 }
714 ops::Include::OP => {
715 let include = ops::Include::decode(cur)?;
716 self.pos = cur.pos;
717 self.exec_include(script, &include, handler)?
718 }
719 ops::Require::OP => {
720 let require = ops::Require::decode(cur)?;
721 self.pos = cur.pos;
722 for rec in script.recs(require.capabilities)? {
723 let capability = Capability::from_rec(script, rec)?;
724 if !self.runtime.allowed_capabilities.contains(&capability) {
725 return Err(if let Capability::Other(not_supported) = capability {
726 RuntimeError::CapabilityNotSupported(not_supported)
727 } else {
728 RuntimeError::CapabilityNotAllowed(capability)
729 });
730 }
731 }
732 Flow::Continue
733 }
734 ops::Error::OP => {
735 let error = ops::Error::decode(cur)?;
736 self.pos = cur.pos;
737 let message = self.eval_value(script, error.message)?;
738 return Err(RuntimeError::ScriptErrorMessage(
739 message.to_string().into_owned(),
740 ));
741 }
742 ops::Invalid::OP => {
743 let invalid = ops::Invalid::decode(cur)?;
744 return Err(RuntimeError::InvalidInstruction {
745 name: script.str(invalid.name)?.to_string(),
746 line_num: invalid.line_num,
747 line_pos: invalid.line_pos,
748 });
749 }
750 #[cfg(test)]
751 ops::TestCmd::OP => {
752 let cmd = ops::TestCmd::decode(cur)?;
753 self.pos = cur.pos;
754 {
755 let result = self.exec_test_cmd(script, cmd.arguments, false, handler)?;
756 self.test_outcome(result)
757 }
758 }
759 #[cfg(test)]
760 ops::TestCmdTest::OP => {
761 let cmd = ops::TestCmdTest::decode(cur)?;
762 self.pos = cur.pos;
763 {
764 let result = self.exec_test_cmd(script, cmd.arguments, cmd.is_not, handler)?;
765 self.test_outcome(result)
766 }
767 }
768 _ => return Err(RuntimeError::InvalidBytecode),
769 };
770 Ok(flow)
771 }
772
773 #[inline(always)]
774 fn test_outcome(&mut self, result: TestResult) -> Flow {
775 match result {
776 TestResult::Bool(value) => {
777 self.test_result = value;
778 Flow::Continue
779 }
780 TestResult::Pending { is_not } => {
781 self.pending = Pending::Test { is_not };
782 Flow::Pending
783 }
784 }
785 }
786
787 #[inline(always)]
788 fn flush<H: Handler<'x>>(&mut self, handler: &mut H) -> Result<Flow, RuntimeError> {
789 if self.oom.get() {
790 return Err(RuntimeError::MemoryLimitReached);
791 }
792 if self.actions.is_empty() || self.flush_actions(handler)? {
793 Ok(Flow::Continue)
794 } else {
795 Ok(Flow::Pending)
796 }
797 }
798
799 fn flush_actions<H: Handler<'x>>(&mut self, handler: &mut H) -> Result<bool, RuntimeError> {
800 let mut actions = std::mem::take(&mut self.actions).into_iter();
801 while let Some(action) = actions.next() {
802 match handler.action(self, action) {
803 Reply::Ready(()) => (),
804 Reply::Pending => {
805 let remaining: Vec<Action<'x>> = actions.collect();
806 self.actions = remaining;
807 self.pending = Pending::Action;
808 return Ok(false);
809 }
810 Reply::Error(err) => return Err(err),
811 }
812 }
813 Ok(true)
814 }
815
816 fn finish<H: Handler<'x>>(&mut self, handler: &mut H) -> Result<Status, RuntimeError> {
817 self.frames.clear();
818 if let Some(action) = self.final_action.take() {
819 match action {
820 Action::Keep { flags, message_id } => {
821 let create_message = if self.has_changes {
822 self.build_message_id()
823 } else {
824 None
825 };
826 let flags = if flags.is_empty() && !self.flags.is_empty() {
827 let flags = std::mem::take(&mut self.flags);
828 let flags = self.alloc_strs(&flags);
829 if self.oom.get() {
830 return Err(self.abort(RuntimeError::MemoryLimitReached));
831 }
832 flags
833 } else {
834 flags
835 };
836 if let Some(create_message) = create_message {
837 self.actions.push(create_message);
838 self.actions.push(Action::Keep {
839 flags,
840 message_id: self.main_message_id,
841 });
842 } else {
843 self.actions.push(Action::Keep { flags, message_id });
844 }
845 }
846 action => self.actions.push(action),
847 }
848 }
849 match self.flush_actions(handler) {
850 Ok(true) => {
851 self.pending = Pending::Finished;
852 Ok(Status::Finished)
853 }
854 Ok(false) => Ok(Status::Pending),
855 Err(err) => Err(self.abort(err)),
856 }
857 }
858
859 fn abort(&mut self, err: RuntimeError) -> RuntimeError {
860 self.frames.clear();
861 self.actions.clear();
862 if self.pending != Pending::Aborted && !self.rejected {
863 self.final_action = Some(Action::Keep {
864 flags: &[],
865 message_id: 0,
866 });
867 }
868 self.pending = Pending::Aborted;
869 err
870 }
871
872 pub fn resume(&mut self, input: impl Into<Input<'x>>) {
873 let input = input.into();
874 match std::mem::replace(&mut self.pending, Pending::Action) {
875 Pending::Test { is_not } => {
876 self.test_result = matches!(input, Input::Bool(true)) ^ is_not;
877 }
878 Pending::Function => {
879 let value = match input {
880 Input::Value(value) => self.intern(value),
881 Input::Bool(value) => Variable::from(value),
882 _ => Variable::default(),
883 };
884 self.expr_stack.push(value);
885 }
886 Pending::Include { name, optional } => match input {
887 Input::Script(Some(script)) => self.push_included(name, script),
888 _ if optional => (),
889 _ => {
890 let err = RuntimeError::ScriptNotFound(name.name().to_string());
891 self.error = Some(self.abort(err));
892 }
893 },
894 Pending::Action => (),
895 Pending::Idle => {
896 self.pending = Pending::Idle;
897 }
898 Pending::Finished => {
899 self.pending = Pending::Finished;
900 }
901 Pending::Aborted => {
902 self.pending = Pending::Aborted;
903 }
904 }
905 }
906
907 fn push_included(&mut self, name: Script<'x>, script: &'x Sieve<'x>) {
908 if !self.included.contains(&name) {
909 self.included.push(name);
910 }
911 self.push_frame(script);
912 }
913
914 pub(crate) fn push_frame(&mut self, script: &'x Sieve<'x>) {
915 self.frames.push(Frame {
916 script: self.script,
917 prev_pos: self.pos,
918 local_base: self.vars_local.len(),
919 match_base: self.vars_match.len(),
920 });
921 self.local_base = self.vars_local.len();
922 self.match_base = self.vars_match.len();
923 self.vars_local
924 .resize_with(self.vars_local.len() + script.num_vars(), Variable::default);
925 self.vars_match.resize_with(
926 self.vars_match.len() + script.num_match_vars(),
927 Variable::default,
928 );
929 self.script = script;
930 self.pos = 0;
931 self.test_result = false;
932 }
933
934 pub(crate) fn pop_frame(&mut self) {
935 if let Some(frame) = self.frames.pop() {
936 self.vars_local.truncate(frame.local_base);
937 self.vars_match.truncate(frame.match_base);
938 self.script = frame.script;
939 self.pos = frame.prev_pos;
940 self.local_base = self.frames.last().map_or(0, |f| f.local_base);
941 self.match_base = self.frames.last().map_or(0, |f| f.match_base);
942 }
943 }
944
945 #[inline(always)]
946 pub(crate) fn local_base(&self) -> usize {
947 self.local_base
948 }
949
950 #[inline(always)]
951 pub(crate) fn match_base(&self) -> usize {
952 self.match_base
953 }
954
955 fn exec_clear(&mut self, clear: &ops::Clear) {
956 if clear.local_vars_num > 0 {
957 let base = self.local_base() + clear.local_vars_idx as usize;
958 if let Some(local_vars) = self
959 .vars_local
960 .get_mut(base..base + clear.local_vars_num as usize)
961 {
962 for local_var in local_vars.iter_mut() {
963 if !local_var.is_empty() {
964 *local_var = Variable::default();
965 }
966 }
967 } else {
968 debug_assert!(false, "Failed to clear local variables: {clear:?}");
969 }
970 }
971 if clear.match_vars != 0 {
972 self.clear_match_variables(clear.match_vars);
973 }
974 }
975
976 fn exec_include<H: Handler<'x>>(
977 &mut self,
978 script: &'x Sieve<'x>,
979 include: &ops::Include,
980 handler: &mut H,
981 ) -> Result<Flow, RuntimeError> {
982 let name = self.eval_value(script, include.value)?.into_string();
983 if name.is_empty() {
984 return Ok(Flow::Continue);
985 }
986 let name = self.intern_cow(name);
987 let script_name = if include.global {
988 Script::Global(name)
989 } else {
990 Script::Personal(name)
991 };
992 if include.once && self.included.contains(&script_name) {
993 return Ok(Flow::Continue);
994 }
995 if self.frames.len() >= self.runtime.max_nested_includes {
996 return Err(RuntimeError::TooManyIncludes);
997 }
998 if let Some(cached) = self.runtime.include_scripts.get(name) {
999 self.push_included(script_name, cached);
1000 return Ok(Flow::Switch);
1001 }
1002 match handler.include_script(self, script_name, include.optional) {
1003 Reply::Ready(Some(included)) => {
1004 self.push_included(script_name, included);
1005 Ok(Flow::Switch)
1006 }
1007 Reply::Ready(None) if include.optional => Ok(Flow::Continue),
1008 Reply::Ready(None) => Err(RuntimeError::ScriptNotFound(name.to_string())),
1009 Reply::Error(err) => Err(err),
1010 Reply::Pending => {
1011 self.pending = Pending::Include {
1012 name: script_name,
1013 optional: include.optional,
1014 };
1015 Ok(Flow::Pending)
1016 }
1017 }
1018 }
1019
1020 #[inline(always)]
1021 fn next_part(&mut self) -> Option<u32> {
1022 let part = self.part_iter.get(self.part_iter_pos).copied()?;
1023 self.part_iter_pos += 1;
1024 Some(part)
1025 }
1026
1027 pub fn set_envelope(
1028 &mut self,
1029 envelope: impl TryInto<Envelope>,
1030 value: impl Into<Cow<'x, str>>,
1031 ) {
1032 if let Ok(envelope) = envelope.try_into() {
1033 if matches!(&envelope, Envelope::From | Envelope::To) {
1034 let value: Cow<str> = value.into();
1035 if let Some(value) = parse_envelope_address(value.as_ref()) {
1036 let value = self.alloc_str(value);
1037 self.envelope.push((envelope, Variable::borrowed(value)));
1038 }
1039 } else {
1040 self.envelope.push((envelope, Variable::from(value.into())));
1041 }
1042 }
1043 }
1044
1045 pub fn with_vars_env(mut self, vars_env: AHashMap<Cow<'static, str>, Variable<'x>>) -> Self {
1046 self.vars_env = vars_env;
1047 self
1048 }
1049
1050 pub fn with_envelope_list(mut self, envelope: Vec<(Envelope, Variable<'x>)>) -> Self {
1051 self.envelope = envelope;
1052 self
1053 }
1054
1055 pub fn with_envelope(
1056 mut self,
1057 envelope: impl TryInto<Envelope>,
1058 value: impl Into<Cow<'x, str>>,
1059 ) -> Self {
1060 self.set_envelope(envelope, value);
1061 self
1062 }
1063
1064 pub fn clear_envelope(&mut self) {
1065 self.envelope.clear()
1066 }
1067
1068 pub fn set_user_address(&mut self, from: impl Into<Cow<'x, str>>) {
1069 self.user_address = from.into();
1070 }
1071
1072 pub fn with_user_address(mut self, from: impl Into<Cow<'x, str>>) -> Self {
1073 self.set_user_address(from);
1074 self
1075 }
1076
1077 pub fn set_user_full_name(&mut self, name: &str) {
1078 let mut name_ = String::with_capacity(name.len());
1079 for ch in name.chars() {
1080 if ['\"', '\\'].contains(&ch) {
1081 name_.push('\\');
1082 }
1083 name_.push(ch);
1084 }
1085 self.user_full_name = name_.into();
1086 }
1087
1088 pub fn with_user_full_name(mut self, name: &str) -> Self {
1089 self.set_user_full_name(name);
1090 self
1091 }
1092
1093 pub fn set_env_variable(
1094 &mut self,
1095 name: impl Into<Cow<'static, str>>,
1096 value: impl Into<Variable<'x>>,
1097 ) {
1098 self.vars_env.insert(name.into(), value.into());
1099 }
1100
1101 pub fn with_env_variable(
1102 mut self,
1103 name: impl Into<Cow<'static, str>>,
1104 value: impl Into<Variable<'x>>,
1105 ) -> Self {
1106 self.set_env_variable(name, value);
1107 self
1108 }
1109
1110 pub fn set_global_variable(
1111 &mut self,
1112 name: impl Into<Cow<'static, str>>,
1113 value: impl Into<Variable<'x>>,
1114 ) {
1115 self.vars_global.insert(name.into(), value.into());
1116 }
1117
1118 pub fn with_global_variable(
1119 mut self,
1120 name: impl Into<Cow<'static, str>>,
1121 value: impl Into<Variable<'x>>,
1122 ) -> Self {
1123 self.set_global_variable(name, value);
1124 self
1125 }
1126
1127 pub fn set_medatata(
1128 &mut self,
1129 name: impl Into<Metadata<String>>,
1130 value: impl Into<Cow<'x, str>>,
1131 ) {
1132 self.metadata.push((name.into(), value.into()));
1133 }
1134
1135 pub fn with_metadata(
1136 mut self,
1137 name: impl Into<Metadata<String>>,
1138 value: impl Into<Cow<'x, str>>,
1139 ) -> Self {
1140 self.set_medatata(name, value);
1141 self
1142 }
1143
1144 pub fn set_spam_status(&mut self, status: impl Into<SpamStatus>) {
1145 self.spam_status = status.into();
1146 }
1147
1148 pub fn with_spam_status(mut self, status: impl Into<SpamStatus>) -> Self {
1149 self.set_spam_status(status);
1150 self
1151 }
1152
1153 pub fn set_virus_status(&mut self, status: impl Into<VirusStatus>) {
1154 self.virus_status = status.into();
1155 }
1156
1157 pub fn with_virus_status(mut self, status: impl Into<VirusStatus>) -> Self {
1158 self.set_virus_status(status);
1159 self
1160 }
1161
1162 pub fn set_current_time(&mut self, time: i64) {
1163 self.current_time = time;
1164 }
1165
1166 pub fn with_current_time(mut self, time: i64) -> Self {
1167 self.current_time = time;
1168 self
1169 }
1170
1171 pub fn take_message(&mut self) -> Message<'x> {
1172 self.raw_message_copy.set(None);
1173 std::mem::take(&mut self.message)
1174 }
1175
1176 pub fn has_message_changed(&self) -> bool {
1177 self.main_message_id > 0
1178 }
1179
1180 pub(crate) fn user_from_field(&self) -> String {
1181 if !self.user_full_name.is_empty() {
1182 format!("\"{}\" <{}>", self.user_full_name, self.user_address)
1183 } else {
1184 self.user_address.to_string()
1185 }
1186 }
1187
1188 pub fn global_variable_names(&self) -> impl Iterator<Item = &str> {
1189 self.vars_global.keys().map(|k| k.as_ref())
1190 }
1191
1192 pub fn global_variable(&self, name: &str) -> Option<&Variable<'_>> {
1193 self.vars_global.get(name)
1194 }
1195
1196 pub fn message(&self) -> &Message<'x> {
1197 &self.message
1198 }
1199
1200 pub fn part(&self) -> u32 {
1201 self.part
1202 }
1203
1204 pub fn runtime(&self) -> &'x Runtime {
1205 self.runtime
1206 }
1207
1208 pub fn instructions_executed(&self) -> usize {
1209 self.num_instructions
1210 }
1211}
1212
1213#[derive(Debug, Clone, Copy, PartialEq, Eq)]
1214pub(crate) enum Flow {
1215 Continue,
1216 Jump(usize),
1217 Switch,
1218 Pending,
1219}
1220
1221#[inline(always)]
1222pub(crate) unsafe fn extend<'x, T: ?Sized>(value: &T) -> &'x T {
1223 unsafe { &*(value as *const T) }
1224}
1225
1226impl Capability {
1227 pub(crate) fn from_rec(script: &Sieve<'_>, rec: Rec) -> Result<Capability, RuntimeError> {
1228 if rec.tag != tag::CAPABILITY {
1229 return Err(RuntimeError::InvalidBytecode);
1230 }
1231 Ok(match rec.b {
1232 5 => {
1233 Capability::Comparator(crate::compiler::grammar::Comparator::from_code(rec.c as u8))
1234 }
1235 6 => Capability::Other(script.str(rec.str())?.to_string()),
1236 id => Capability::from_id(id),
1237 })
1238 }
1239}
1240
1241#[cfg(test)]
1242impl<'x> Context<'x> {
1243 pub(crate) fn with_runtime(self, runtime: &'x Runtime) -> Context<'x> {
1244 Context { runtime, ..self }
1245 }
1246
1247 pub(crate) fn set_message(&mut self, message: Message<'x>, size: usize) {
1248 self.raw_message_copy.set(None);
1249 self.message = message;
1250 self.message_size = size;
1251 self.part = 0;
1252 }
1253
1254 pub(crate) fn exec_test_cmd<H: Handler<'x>>(
1255 &mut self,
1256 script: &'x Sieve<'x>,
1257 arguments: crate::bytecode::rec::Range,
1258 is_not: bool,
1259 handler: &mut H,
1260 ) -> Result<super::tests::TestResult, RuntimeError> {
1261 let arguments = self.eval_values(script, arguments)?;
1262 match handler.function(self, u32::MAX, &arguments) {
1263 Reply::Ready(value) => Ok(super::tests::TestResult::Bool(value.to_bool() ^ is_not)),
1264 Reply::Pending => Ok(super::tests::TestResult::Pending { is_not }),
1265 Reply::Error(err) => Err(err),
1266 }
1267 }
1268}