postfix_log_parser/components/
trivial_rewrite.rs1use crate::components::ComponentParser;
2use crate::events::trivial_rewrite::TrivialRewriteEvent;
3use crate::events::ComponentEvent;
4use chrono::Utc;
5use lazy_static::lazy_static;
6use regex::Regex;
7
8lazy_static! {
9 static ref CONFIG_OVERRIDE_WARNING_REGEX: Regex = Regex::new(
12 r"^(.+?), line (\d+): overriding earlier entry: (.+?)=(.+)$"
13 ).unwrap();
14
15 static ref DOMAIN_CONFIG_WARNING_REGEX: Regex = Regex::new(
18 r"^do not list domain (.+?) in BOTH (.+?) and (.+?)$"
19 ).unwrap();
20}
21
22pub struct TrivialRewriteParser;
23
24impl TrivialRewriteParser {
25 pub fn new() -> Self {
26 Self
27 }
28
29 fn parse_config_override_warning(&self, message: &str) -> Option<TrivialRewriteEvent> {
33 if let Some(captures) = CONFIG_OVERRIDE_WARNING_REGEX.captures(message) {
34 let file_path = captures.get(1)?.as_str().to_string();
35 let line_number: u32 = captures.get(2)?.as_str().parse().ok()?;
36 let parameter_name = captures.get(3)?.as_str().to_string();
37 let parameter_value = captures.get(4)?.as_str().to_string();
38
39 Some(TrivialRewriteEvent::config_override_warning(
40 Utc::now(),
41 None,
42 file_path,
43 line_number,
44 parameter_name,
45 parameter_value,
46 ))
47 } else {
48 None
49 }
50 }
51
52 fn parse_domain_config_warning(&self, message: &str) -> Option<TrivialRewriteEvent> {
56 if let Some(captures) = DOMAIN_CONFIG_WARNING_REGEX.captures(message) {
57 let domain = captures.get(1)?.as_str().to_string();
58 let domain_list1 = captures.get(2)?.as_str().to_string();
59 let domain_list2 = captures.get(3)?.as_str().to_string();
60 let full_message = message.to_string(); Some(TrivialRewriteEvent::domain_config_warning(
63 Utc::now(),
64 None,
65 domain,
66 domain_list1,
67 domain_list2,
68 full_message,
69 ))
70 } else {
71 None
72 }
73 }
74}
75
76impl ComponentParser for TrivialRewriteParser {
77 fn parse(&self, message: &str) -> Result<ComponentEvent, crate::error::ParseError> {
78 let clean_message = message.trim();
79
80 if let Some(event) = self.parse_config_override_warning(clean_message) {
82 return Ok(ComponentEvent::TrivialRewrite(event));
83 }
84
85 if let Some(event) = self.parse_domain_config_warning(clean_message) {
87 return Ok(ComponentEvent::TrivialRewrite(event));
88 }
89
90 Err(crate::error::ParseError::ComponentParseError {
91 component: "trivial-rewrite".to_string(),
92 reason: format!("Unable to parse message: {}", message),
93 })
94 }
95
96 fn component_name(&self) -> &'static str {
97 "trivial-rewrite"
98 }
99}
100
101#[cfg(test)]
102mod tests {
103 use super::*;
104 use crate::events::trivial_rewrite::TrivialRewriteEventType;
105
106 fn create_parser() -> TrivialRewriteParser {
107 TrivialRewriteParser::new()
108 }
109
110 #[test]
111 fn test_parse_config_override_warning() {
112 let parser = create_parser();
113
114 let message = "/etc/postfix/main.cf, line 820: overriding earlier entry: smtpd_recipient_restrictions=check_client_access pcre:/etc/postfix/filter_trusted,permit_sasl_authenticated,permit_mynetworks,reject_unauth_destination,pcre:/etc/postfix/filter_default";
115 let result = parser.parse_config_override_warning(message);
116
117 assert!(result.is_some());
118 let event = result.unwrap();
119
120 match &event.event_type {
121 TrivialRewriteEventType::ConfigOverrideWarning {
122 file_path,
123 line_number,
124 parameter_name,
125 parameter_value,
126 } => {
127 assert_eq!(file_path, "/etc/postfix/main.cf");
128 assert_eq!(*line_number, 820);
129 assert_eq!(parameter_name, "smtpd_recipient_restrictions");
130 assert!(parameter_value.contains("check_client_access"));
131 }
132 _ => panic!("Expected ConfigOverrideWarning event type"),
133 }
134 }
135
136 #[test]
137 fn test_parse_domain_config_warning() {
138 let parser = create_parser();
139
140 let message = "do not list domain qq.com in BOTH virtual_alias_domains and relay_domains";
141 let result = parser.parse_domain_config_warning(message);
142
143 assert!(result.is_some());
144 let event = result.unwrap();
145
146 match &event.event_type {
147 TrivialRewriteEventType::DomainConfigWarning {
148 domain,
149 domain_list1,
150 domain_list2,
151 message: full_message,
152 } => {
153 assert_eq!(domain, "qq.com");
154 assert_eq!(domain_list1, "virtual_alias_domains");
155 assert_eq!(domain_list2, "relay_domains");
156 assert_eq!(
157 full_message,
158 "do not list domain qq.com in BOTH virtual_alias_domains and relay_domains"
159 );
160 }
161 _ => panic!("Expected DomainConfigWarning event type"),
162 }
163 }
164
165 #[test]
166 fn test_component_parser_parse_config_override() {
167 let parser = create_parser();
168
169 let message = "/etc/postfix/main.cf, line 806: overriding earlier entry: smtpd_client_message_rate_limit=0";
170 let result = parser.parse(message);
171
172 assert!(result.is_ok());
173 match result.unwrap() {
174 ComponentEvent::TrivialRewrite(event) => {
175 assert_eq!(event.severity(), "warning");
176 assert_eq!(
177 event.parameter_name(),
178 Some("smtpd_client_message_rate_limit")
179 );
180 }
181 _ => panic!("Expected TrivialRewrite ComponentEvent"),
182 }
183 }
184
185 #[test]
186 fn test_component_parser_parse_domain_config() {
187 let parser = create_parser();
188
189 let message = "do not list domain qq.com in BOTH virtual_alias_domains and relay_domains";
190 let result = parser.parse(message);
191
192 assert!(result.is_ok());
193 match result.unwrap() {
194 ComponentEvent::TrivialRewrite(event) => {
195 assert_eq!(event.severity(), "warning");
196 assert_eq!(event.domain(), Some("qq.com"));
197 }
198 _ => panic!("Expected TrivialRewrite ComponentEvent"),
199 }
200 }
201
202 #[test]
203 fn test_component_parser_parse_invalid() {
204 let parser = create_parser();
205
206 let message = "some unknown message format";
207 let result = parser.parse(message);
208
209 assert!(result.is_err());
210 match result.unwrap_err() {
211 crate::error::ParseError::ComponentParseError { component, .. } => {
212 assert_eq!(component, "trivial-rewrite");
213 }
214 _ => panic!("Expected ComponentParseError"),
215 }
216 }
217
218 #[test]
219 fn test_component_name() {
220 let parser = create_parser();
221 assert_eq!(parser.component_name(), "trivial-rewrite");
222 }
223
224 #[test]
225 fn test_parse_real_log_samples() {
226 let parser = create_parser();
227
228 let message1 = "/etc/postfix/main.cf, line 819: overriding earlier entry: smtpd_recipient_restrictions=check_client_access pcre:/etc/postfix/filter_trusted,permit_sasl_authenticated,permit_mynetworks,reject_unauth_destination,pcre:/etc/postfix/filter_default";
230 let result1 = parser.parse(message1);
231 assert!(result1.is_ok());
232
233 let message2 = "do not list domain qq.com in BOTH virtual_alias_domains and relay_domains";
235 let result2 = parser.parse(message2);
236 assert!(result2.is_ok());
237
238 let message3 = "/etc/postfix/main.cf, line 826: overriding earlier entry: smtpd_discard_ehlo_keywords=silent-discard,dsn,etrn";
240 let result3 = parser.parse(message3);
241 assert!(result3.is_ok());
242
243 match result3.unwrap() {
244 ComponentEvent::TrivialRewrite(event) => {
245 assert_eq!(event.parameter_name(), Some("smtpd_discard_ehlo_keywords"));
246 }
247 _ => panic!("Expected TrivialRewrite ComponentEvent"),
248 }
249 }
250}