sieve/compiler/grammar/actions/
action_editheader.rs1use mail_parser::HeaderName;
25use serde::{Deserialize, Serialize};
26
27use crate::compiler::{
28 grammar::{
29 instruction::{CompilerState, Instruction},
30 Capability, Comparator,
31 },
32 lexer::{word::Word, Token},
33 CompileError, ErrorType, Value,
34};
35
36use crate::compiler::grammar::MatchType;
37
38#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
39pub struct AddHeader {
40 pub last: bool,
41 pub field_name: Value,
42 pub value: Value,
43}
44
45#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
53pub struct DeleteHeader {
54 pub index: Option<i32>,
55 pub comparator: Comparator,
56 pub match_type: MatchType,
57 pub field_name: Value,
58 pub value_patterns: Vec<Value>,
59 pub mime_anychild: bool,
60}
61
62impl<'x> CompilerState<'x> {
63 pub(crate) fn parse_addheader(&mut self) -> Result<(), CompileError> {
64 let mut field_name = None;
65 let value;
66 let mut last = false;
67
68 loop {
69 let token_info = self.tokens.unwrap_next()?;
70 match token_info.token {
71 Token::Tag(Word::Last) => {
72 self.validate_argument(1, None, token_info.line_num, token_info.line_pos)?;
73 last = true;
74 }
75 _ => {
76 let string = self.parse_string_token(token_info)?;
77 if field_name.is_none() {
78 if let Value::Text(header_name) = &string {
79 if HeaderName::parse(header_name.as_ref()).is_none() {
80 return Err(self
81 .tokens
82 .unwrap_next()?
83 .custom(ErrorType::InvalidHeaderName));
84 }
85 }
86
87 field_name = string.into();
88 } else {
89 if matches!(
90 &string,
91 Value::Text(value) if value.len() > self.compiler.max_header_size
92 ) {
93 return Err(self
94 .tokens
95 .unwrap_next()?
96 .custom(ErrorType::HeaderTooLong));
97 }
98 value = string;
99 break;
100 }
101 }
102 }
103 }
104 self.instructions.push(Instruction::AddHeader(AddHeader {
105 last,
106 field_name: field_name.unwrap(),
107 value,
108 }));
109 Ok(())
110 }
111
112 pub(crate) fn parse_deleteheader(&mut self) -> Result<(), CompileError> {
113 let field_name: Value;
114 let mut match_type = MatchType::Is;
115 let mut comparator = Comparator::AsciiCaseMap;
116 let mut index = None;
117 let mut index_last = false;
118 let mut mime = false;
119 let mut mime_anychild = false;
120
121 loop {
122 let token_info = self.tokens.unwrap_next()?;
123 match token_info.token {
124 Token::Tag(
125 word @ (Word::Is
126 | Word::Contains
127 | Word::Matches
128 | Word::Value
129 | Word::Count
130 | Word::Regex),
131 ) => {
132 self.validate_argument(
133 1,
134 match word {
135 Word::Value | Word::Count => Capability::Relational.into(),
136 Word::Regex => Capability::Regex.into(),
137 Word::List => Capability::ExtLists.into(),
138 _ => None,
139 },
140 token_info.line_num,
141 token_info.line_pos,
142 )?;
143 match_type = self.parse_match_type(word)?;
144 }
145 Token::Tag(Word::Comparator) => {
146 self.validate_argument(2, None, token_info.line_num, token_info.line_pos)?;
147 comparator = self.parse_comparator()?;
148 }
149 Token::Tag(Word::Index) => {
150 self.validate_argument(3, None, token_info.line_num, token_info.line_pos)?;
151 index = (self.tokens.expect_number(u16::MAX as usize)? as i32).into();
152 }
153 Token::Tag(Word::Last) => {
154 self.validate_argument(4, None, token_info.line_num, token_info.line_pos)?;
155 index_last = true;
156 }
157 Token::Tag(Word::Mime) => {
158 self.validate_argument(
159 5,
160 Capability::Mime.into(),
161 token_info.line_num,
162 token_info.line_pos,
163 )?;
164 mime = true;
165 }
166 Token::Tag(Word::AnyChild) => {
167 self.validate_argument(
168 6,
169 Capability::Mime.into(),
170 token_info.line_num,
171 token_info.line_pos,
172 )?;
173 mime_anychild = true;
174 }
175 _ => {
176 field_name = self.parse_string_token(token_info)?;
177 if let Value::Text(header_name) = &field_name {
178 if HeaderName::parse(header_name.as_ref()).is_none() {
179 return Err(self
180 .tokens
181 .unwrap_next()?
182 .custom(ErrorType::InvalidHeaderName));
183 }
184 }
185 break;
186 }
187 }
188 }
189
190 if !mime && mime_anychild {
191 return Err(self.tokens.unwrap_next()?.missing_tag(":mime"));
192 }
193
194 let cmd = Instruction::DeleteHeader(DeleteHeader {
195 index: if index_last { index.map(|i| -i) } else { index },
196 comparator,
197 match_type,
198 field_name,
199 value_patterns: if let Some(Ok(
200 Token::StringConstant(_) | Token::StringVariable(_) | Token::BracketOpen,
201 )) = self.tokens.peek().map(|r| r.map(|t| &t.token))
202 {
203 let mut key_list = self.parse_strings(false)?;
204 self.validate_match(&match_type, &mut key_list)?;
205 key_list
206 } else {
207 Vec::new()
208 },
209 mime_anychild,
210 });
211 self.instructions.push(cmd);
212 Ok(())
213 }
214}