sieve/runtime/actions/
action_editheader.rs1use std::borrow::Cow;
8
9use mail_parser::{Header, HeaderName, HeaderValue};
10
11use crate::{
12 compiler::grammar::{
13 actions::{
14 action_editheader::{AddHeader, DeleteHeader},
15 action_mime::MimeOpts,
16 },
17 MatchType,
18 },
19 Context,
20};
21
22impl AddHeader {
23 pub(crate) fn exec(&self, ctx: &mut Context) {
24 let header_name__ = ctx.eval_value(&self.field_name);
25 let header_name_ = header_name__.to_string();
26 let mut header_name = String::with_capacity(header_name_.len());
27
28 for ch in header_name_.chars() {
29 if ch.is_alphanumeric() || ch == '-' {
30 header_name.push(ch);
31 }
32 }
33
34 if !header_name.is_empty() {
35 if let Some(header_name) = HeaderName::parse(header_name) {
36 if !ctx.runtime.protected_headers.contains(&header_name) {
37 ctx.has_changes = true;
38 ctx.insert_header(
39 ctx.part,
40 header_name,
41 ctx.eval_value(&self.value)
42 .to_string()
43 .as_ref()
44 .remove_crlf(ctx.runtime.max_header_size),
45 self.last,
46 )
47 }
48 }
49 }
50 }
51}
52
53impl DeleteHeader {
54 pub(crate) fn exec(&self, ctx: &mut Context) {
55 let header_name__ = ctx.eval_value(&self.field_name);
56 let header_name_ = header_name__.to_string();
57 let header_name = if let Some(header_name) = HeaderName::parse(header_name_.as_ref()) {
58 header_name
59 } else {
60 return;
61 };
62 let value_patterns = ctx.eval_values(&self.value_patterns);
63 let mut deleted_headers = Vec::new();
64 let mut deleted_bytes = 0;
65
66 if ctx.runtime.protected_headers.contains(&header_name) {
67 return;
68 }
69
70 ctx.find_headers(
71 &[header_name],
72 self.index,
73 self.mime_anychild,
74 |header, part_id, header_pos| {
75 if !value_patterns.is_empty() {
76 let did_match = ctx.find_header_values(header, &MimeOpts::None, |value| {
77 for (pattern_expr, pattern) in
78 value_patterns.iter().zip(self.value_patterns.iter())
79 {
80 if match &self.match_type {
81 MatchType::Is => self.comparator.is(&value, pattern_expr),
82 MatchType::Contains => self
83 .comparator
84 .contains(value, pattern_expr.to_string().as_ref()),
85 MatchType::Value(rel_match) => {
86 self.comparator.relational(rel_match, &value, pattern_expr)
87 }
88 MatchType::Matches(_) => self.comparator.matches(
89 value,
90 pattern_expr.to_string().as_ref(),
91 0,
92 &mut Vec::new(),
93 ),
94 MatchType::Regex(_) => self.comparator.regex(
95 pattern,
96 pattern_expr,
97 value,
98 0,
99 &mut Vec::new(),
100 ),
101 MatchType::Count(_) => false,
102 MatchType::List => false,
103 } {
104 return true;
105 }
106 }
107 false
108 });
109
110 if !did_match {
111 return false;
112 }
113 }
114
115 if header.offset_end != 0 {
116 deleted_bytes += (header.offset_end - header.offset_field) as usize;
117 } else {
118 deleted_bytes += header.name.as_str().len() + header.value.len() + 4;
119 }
120 deleted_headers.push((part_id, header_pos));
121
122 false
123 },
124 );
125
126 if !deleted_headers.is_empty() {
127 ctx.has_changes = true;
128 for (part_id, header_pos) in deleted_headers.iter().rev() {
129 ctx.message.parts[*part_id as usize]
130 .headers
131 .remove(*header_pos);
132 }
133 }
134
135 ctx.message_size -= deleted_bytes;
136 }
137}
138
139pub(crate) trait RemoveCrLf {
140 fn remove_crlf(&self, max_len: usize) -> String;
141}
142
143impl RemoveCrLf for &str {
144 fn remove_crlf(&self, max_len: usize) -> String {
145 let mut header_value = String::with_capacity(self.len());
146 for ch in self.chars() {
147 if !['\n', '\r'].contains(&ch) {
148 if header_value.len() + ch.len_utf8() <= max_len {
149 header_value.push(ch);
150 } else {
151 return header_value;
152 }
153 }
154 }
155 header_value
156 }
157}
158
159impl<'x> Context<'x> {
160 pub(crate) fn insert_header(
161 &mut self,
162 part_id: u32,
163 header_name: HeaderName<'x>,
164 header_value: impl Into<Cow<'static, str>>,
165 last: bool,
166 ) {
167 let header_value = header_value.into();
168 self.message_size += header_name.len() + header_value.len() + 4;
169 let header = Header {
170 name: header_name,
171 value: HeaderValue::Text(header_value),
172 offset_start: 0,
173 offset_end: 0,
174 offset_field: 0,
175 };
176
177 if !last {
178 self.message.parts[part_id as usize]
179 .headers
180 .insert(0, header);
181 } else {
182 self.message.parts[part_id as usize].headers.push(header);
183 }
184 }
185}