postfix_log_parser/events/
trivial_rewrite.rs

1//! Trivial-rewrite地址重写模块
2//!
3//! 处理Postfix trivial-rewrite守护进程的事件,包括地址重写、域名解析和配置验证
4
5use chrono::{DateTime, Utc};
6use serde::{Deserialize, Serialize};
7
8/// Trivial-rewrite地址重写事件
9///
10/// 记录地址重写和解析组件的配置警告和域名冲突信息
11#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
12pub struct TrivialRewriteEvent {
13    /// 事件发生时间戳(UTC时间)
14    pub timestamp: DateTime<Utc>,
15
16    /// 进程ID(可选)
17    /// trivial-rewrite进程的系统标识符
18    pub pid: Option<u32>,
19
20    /// 事件类型(配置覆盖警告或域名配置警告)
21    pub event_type: TrivialRewriteEventType,
22}
23
24#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
25pub enum TrivialRewriteEventType {
26    ConfigOverrideWarning {
27        file_path: String,
28        line_number: u32,
29        parameter_name: String,
30        parameter_value: String,
31    },
32    DomainConfigWarning {
33        domain: String,
34        domain_list1: String, // e.g., "virtual_alias_domains"
35        domain_list2: String, // e.g., "relay_domains"
36        message: String,
37    },
38}
39
40impl TrivialRewriteEvent {
41    pub fn new(
42        timestamp: DateTime<Utc>,
43        pid: Option<u32>,
44        event_type: TrivialRewriteEventType,
45    ) -> Self {
46        Self {
47            timestamp,
48            pid,
49            event_type,
50        }
51    }
52
53    pub fn config_override_warning(
54        timestamp: DateTime<Utc>,
55        pid: Option<u32>,
56        file_path: String,
57        line_number: u32,
58        parameter_name: String,
59        parameter_value: String,
60    ) -> Self {
61        Self::new(
62            timestamp,
63            pid,
64            TrivialRewriteEventType::ConfigOverrideWarning {
65                file_path,
66                line_number,
67                parameter_name,
68                parameter_value,
69            },
70        )
71    }
72
73    pub fn domain_config_warning(
74        timestamp: DateTime<Utc>,
75        pid: Option<u32>,
76        domain: String,
77        domain_list1: String,
78        domain_list2: String,
79        message: String,
80    ) -> Self {
81        Self::new(
82            timestamp,
83            pid,
84            TrivialRewriteEventType::DomainConfigWarning {
85                domain,
86                domain_list1,
87                domain_list2,
88                message,
89            },
90        )
91    }
92
93    /// Get a human-readable description of the event
94    pub fn description(&self) -> String {
95        match &self.event_type {
96            TrivialRewriteEventType::ConfigOverrideWarning {
97                file_path,
98                line_number,
99                parameter_name,
100                parameter_value,
101            } => {
102                format!(
103                    "Config override in {}:{} - parameter '{}' set to '{}'",
104                    file_path, line_number, parameter_name, parameter_value
105                )
106            }
107            TrivialRewriteEventType::DomainConfigWarning {
108                domain,
109                domain_list1,
110                domain_list2,
111                message,
112            } => {
113                format!(
114                    "Domain config warning for '{}': {} (conflicts between {} and {})",
115                    domain, message, domain_list1, domain_list2
116                )
117            }
118        }
119    }
120
121    /// Get event severity for monitoring/alerting
122    pub fn severity(&self) -> &'static str {
123        match &self.event_type {
124            TrivialRewriteEventType::ConfigOverrideWarning { .. } => "warning",
125            TrivialRewriteEventType::DomainConfigWarning { .. } => "warning",
126        }
127    }
128
129    /// Get the configuration parameter name if applicable
130    pub fn parameter_name(&self) -> Option<&str> {
131        match &self.event_type {
132            TrivialRewriteEventType::ConfigOverrideWarning { parameter_name, .. } => {
133                Some(parameter_name)
134            }
135            TrivialRewriteEventType::DomainConfigWarning { .. } => None,
136        }
137    }
138
139    /// Get the domain name if applicable  
140    pub fn domain(&self) -> Option<&str> {
141        match &self.event_type {
142            TrivialRewriteEventType::ConfigOverrideWarning { .. } => None,
143            TrivialRewriteEventType::DomainConfigWarning { domain, .. } => Some(domain),
144        }
145    }
146}
147
148#[cfg(test)]
149mod tests {
150    use super::*;
151    use chrono::TimeZone;
152
153    #[test]
154    fn test_config_override_warning_event() {
155        let timestamp = Utc.with_ymd_and_hms(2024, 4, 8, 17, 54, 42).unwrap();
156        let event = TrivialRewriteEvent::config_override_warning(
157            timestamp,
158            Some(81),
159            "/etc/postfix/main.cf".to_string(),
160            820,
161            "smtpd_recipient_restrictions".to_string(),
162            "check_client_access pcre:/etc/postfix/filter_trusted,permit_sasl_authenticated,permit_mynetworks,reject_unauth_destination,pcre:/etc/postfix/filter_default".to_string(),
163        );
164
165        assert_eq!(event.timestamp, timestamp);
166        assert_eq!(event.pid, Some(81));
167        assert_eq!(event.severity(), "warning");
168        assert_eq!(event.parameter_name(), Some("smtpd_recipient_restrictions"));
169        assert!(event.description().contains("Config override"));
170        assert!(event.description().contains("/etc/postfix/main.cf:820"));
171    }
172
173    #[test]
174    fn test_domain_config_warning_event() {
175        let timestamp = Utc.with_ymd_and_hms(2024, 4, 9, 14, 42, 34).unwrap();
176        let event = TrivialRewriteEvent::domain_config_warning(
177            timestamp,
178            Some(81),
179            "qq.com".to_string(),
180            "virtual_alias_domains".to_string(),
181            "relay_domains".to_string(),
182            "do not list domain qq.com in BOTH virtual_alias_domains and relay_domains".to_string(),
183        );
184
185        assert_eq!(event.timestamp, timestamp);
186        assert_eq!(event.pid, Some(81));
187        assert_eq!(event.severity(), "warning");
188        assert_eq!(event.domain(), Some("qq.com"));
189        assert!(event.description().contains("Domain config warning"));
190        assert!(event.description().contains("qq.com"));
191        assert!(event
192            .description()
193            .contains("virtual_alias_domains and relay_domains"));
194    }
195
196    #[test]
197    fn test_event_equality() {
198        let timestamp = Utc.with_ymd_and_hms(2024, 4, 8, 17, 54, 42).unwrap();
199
200        let event1 = TrivialRewriteEvent::config_override_warning(
201            timestamp,
202            Some(81),
203            "/etc/postfix/main.cf".to_string(),
204            820,
205            "smtpd_recipient_restrictions".to_string(),
206            "value1".to_string(),
207        );
208
209        let event2 = TrivialRewriteEvent::config_override_warning(
210            timestamp,
211            Some(81),
212            "/etc/postfix/main.cf".to_string(),
213            820,
214            "smtpd_recipient_restrictions".to_string(),
215            "value1".to_string(),
216        );
217
218        assert_eq!(event1, event2);
219    }
220}