Skip to main content

sieve/runtime/
context.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
7use 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, &notify)?;
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}