sieve/compiler/grammar/tests/
test_mailbox.rs

1/*
2 * Copyright (c) 2020-2023, Stalwart Labs Ltd.
3 *
4 * This file is part of the Stalwart Sieve Interpreter.
5 *
6 * This program is free software: you can redistribute it and/or modify
7 * it under the terms of the GNU Affero General Public License as
8 * published by the Free Software Foundation, either version 3 of
9 * the License, or (at your option) any later version.
10 *
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU Affero General Public License for more details.
15 * in the LICENSE file at the top-level directory of this distribution.
16 * You should have received a copy of the GNU Affero General Public License
17 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
18 *
19 * You can be released from the requirements of the AGPLv3 license by
20 * purchasing a commercial license. Please contact licensing@stalw.art
21 * for more details.
22*/
23
24use serde::{Deserialize, Serialize};
25
26use crate::{
27    compiler::{
28        grammar::{
29            instruction::{CompilerState, MapLocalVars},
30            Capability, Comparator,
31        },
32        lexer::{word::Word, Token},
33        CompileError, Value,
34    },
35    Metadata,
36};
37
38use crate::compiler::grammar::{test::Test, MatchType};
39
40#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
41pub(crate) struct TestMailboxExists {
42    pub mailbox_names: Vec<Value>,
43    pub is_not: bool,
44}
45
46#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
47pub(crate) struct TestMetadataExists {
48    pub mailbox: Option<Value>,
49    pub annotation_names: Vec<Value>,
50    pub is_not: bool,
51}
52
53/*
54
55metadata [MATCH-TYPE] [COMPARATOR]
56           <mailbox: string>
57           <annotation-name: string> <key-list: string-list>
58
59*/
60
61#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
62pub(crate) struct TestMetadata {
63    pub match_type: MatchType,
64    pub comparator: Comparator,
65    pub medatata: Metadata<Value>,
66    pub key_list: Vec<Value>,
67    pub is_not: bool,
68}
69
70/*
71
72servermetadata [MATCH-TYPE] [COMPARATOR]
73           <annotation-name: string> <key-list: string-list>
74
75*/
76
77impl<'x> CompilerState<'x> {
78    pub(crate) fn parse_test_mailboxexists(&mut self) -> Result<Test, CompileError> {
79        Ok(Test::MailboxExists(TestMailboxExists {
80            mailbox_names: self.parse_strings(false)?,
81            is_not: false,
82        }))
83    }
84
85    pub(crate) fn parse_test_metadataexists(&mut self) -> Result<Test, CompileError> {
86        Ok(Test::MetadataExists(TestMetadataExists {
87            mailbox: self.parse_string()?.into(),
88            annotation_names: self.parse_strings(false)?,
89            is_not: false,
90        }))
91    }
92
93    pub(crate) fn parse_test_servermetadataexists(&mut self) -> Result<Test, CompileError> {
94        Ok(Test::MetadataExists(TestMetadataExists {
95            mailbox: None,
96            annotation_names: self.parse_strings(false)?,
97            is_not: false,
98        }))
99    }
100
101    pub(crate) fn parse_test_metadata(&mut self) -> Result<Test, CompileError> {
102        let mut match_type = MatchType::Is;
103        let mut comparator = Comparator::AsciiCaseMap;
104        let mut mailbox = None;
105        let mut annotation_name = None;
106        let mut key_list: Vec<Value>;
107
108        loop {
109            let token_info = self.tokens.unwrap_next()?;
110            match token_info.token {
111                Token::Tag(
112                    word @ (Word::Is
113                    | Word::Contains
114                    | Word::Matches
115                    | Word::Value
116                    | Word::Count
117                    | Word::Regex),
118                ) => {
119                    self.validate_argument(
120                        1,
121                        match word {
122                            Word::Value | Word::Count => Capability::Relational.into(),
123                            Word::Regex => Capability::Regex.into(),
124                            Word::List => Capability::ExtLists.into(),
125                            _ => None,
126                        },
127                        token_info.line_num,
128                        token_info.line_pos,
129                    )?;
130
131                    match_type = self.parse_match_type(word)?;
132                }
133                Token::Tag(Word::Comparator) => {
134                    self.validate_argument(2, None, token_info.line_num, token_info.line_pos)?;
135                    comparator = self.parse_comparator()?;
136                }
137                _ => {
138                    if mailbox.is_none() {
139                        mailbox = self.parse_string_token(token_info)?.into();
140                    } else if annotation_name.is_none() {
141                        annotation_name = self.parse_string_token(token_info)?.into();
142                    } else {
143                        key_list = self.parse_strings_token(token_info)?;
144                        break;
145                    }
146                }
147            }
148        }
149        self.validate_match(&match_type, &mut key_list)?;
150
151        Ok(Test::Metadata(TestMetadata {
152            match_type,
153            comparator,
154            medatata: Metadata::Mailbox {
155                name: mailbox.unwrap(),
156                annotation: annotation_name.unwrap(),
157            },
158            key_list,
159            is_not: false,
160        }))
161    }
162
163    pub(crate) fn parse_test_servermetadata(&mut self) -> Result<Test, CompileError> {
164        let mut match_type = MatchType::Is;
165        let mut comparator = Comparator::AsciiCaseMap;
166        let mut annotation_name = None;
167        let mut key_list: Vec<Value>;
168
169        loop {
170            let token_info = self.tokens.unwrap_next()?;
171            match token_info.token {
172                Token::Tag(
173                    word @ (Word::Is
174                    | Word::Contains
175                    | Word::Matches
176                    | Word::Value
177                    | Word::Count
178                    | Word::Regex),
179                ) => {
180                    self.validate_argument(
181                        1,
182                        match word {
183                            Word::Value | Word::Count => Capability::Relational.into(),
184                            Word::Regex => Capability::Regex.into(),
185                            Word::List => Capability::ExtLists.into(),
186                            _ => None,
187                        },
188                        token_info.line_num,
189                        token_info.line_pos,
190                    )?;
191
192                    match_type = self.parse_match_type(word)?;
193                }
194                Token::Tag(Word::Comparator) => {
195                    self.validate_argument(2, None, token_info.line_num, token_info.line_pos)?;
196                    comparator = self.parse_comparator()?;
197                }
198                _ => {
199                    if annotation_name.is_none() {
200                        annotation_name = self.parse_string_token(token_info)?.into();
201                    } else {
202                        key_list = self.parse_strings_token(token_info)?;
203                        break;
204                    }
205                }
206            }
207        }
208        self.validate_match(&match_type, &mut key_list)?;
209
210        Ok(Test::Metadata(TestMetadata {
211            match_type,
212            comparator,
213            medatata: Metadata::Server {
214                annotation: annotation_name.unwrap(),
215            },
216            key_list,
217            is_not: false,
218        }))
219    }
220}
221
222impl MapLocalVars for Metadata<Value> {
223    fn map_local_vars(&mut self, last_id: usize) {
224        match self {
225            Metadata::Mailbox { name, annotation } => {
226                name.map_local_vars(last_id);
227                annotation.map_local_vars(last_id);
228            }
229            Metadata::Server { annotation } => {
230                annotation.map_local_vars(last_id);
231            }
232        }
233    }
234}