postfix_log_parser/events/
postlogd.rs

1use serde::{Deserialize, Serialize};
2use std::fmt;
3
4/// POSTLOGD events - Postfix internal log server
5#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
6pub struct PostlogdEvent {
7    pub timestamp: String,
8    pub process_id: String,
9    pub event_type: PostlogdEventType,
10}
11
12#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
13pub enum PostlogdEventType {
14    /// Configuration override warning
15    ConfigOverrideWarning {
16        file_path: String,
17        line_number: u32,
18        parameter: String,
19        value: String,
20    },
21}
22
23impl fmt::Display for PostlogdEvent {
24    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
25        write!(
26            f,
27            "[{}] postlogd[{}]: {}",
28            self.timestamp, self.process_id, self.event_type
29        )
30    }
31}
32
33impl fmt::Display for PostlogdEventType {
34    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
35        match self {
36            PostlogdEventType::ConfigOverrideWarning {
37                file_path,
38                line_number,
39                parameter,
40                value,
41            } => {
42                write!(
43                    f,
44                    "Config override warning: {}, line {}: overriding earlier entry: {}={}",
45                    file_path, line_number, parameter, value
46                )
47            }
48        }
49    }
50}
51
52#[cfg(test)]
53mod tests {
54    use super::*;
55
56    #[test]
57    fn test_config_override_warning_event() {
58        let event = PostlogdEvent {
59            timestamp: "Apr 08 17:54:30".to_string(),
60            process_id: "78".to_string(),
61            event_type: PostlogdEventType::ConfigOverrideWarning {
62                file_path: "/etc/postfix/main.cf".to_string(),
63                line_number: 820,
64                parameter: "smtpd_recipient_restrictions".to_string(),
65                value: "check_client_access pcre:/etc/postfix/filter_trusted".to_string(),
66            },
67        };
68
69        assert_eq!(event.process_id, "78");
70
71        let PostlogdEventType::ConfigOverrideWarning {
72            file_path,
73            line_number,
74            parameter,
75            ..
76        } = &event.event_type;
77
78        assert_eq!(file_path, "/etc/postfix/main.cf");
79        assert_eq!(*line_number, 820);
80        assert_eq!(parameter, "smtpd_recipient_restrictions");
81    }
82
83    #[test]
84    fn test_event_serialization() {
85        let event = PostlogdEvent {
86            timestamp: "Apr 08 17:54:30".to_string(),
87            process_id: "78".to_string(),
88            event_type: PostlogdEventType::ConfigOverrideWarning {
89                file_path: "/etc/postfix/main.cf".to_string(),
90                line_number: 806,
91                parameter: "smtpd_client_message_rate_limit".to_string(),
92                value: "0".to_string(),
93            },
94        };
95
96        let serialized = serde_json::to_string(&event).unwrap();
97        let deserialized: PostlogdEvent = serde_json::from_str(&serialized).unwrap();
98        assert_eq!(event, deserialized);
99    }
100
101    #[test]
102    fn test_event_display() {
103        let event = PostlogdEvent {
104            timestamp: "Apr 08 17:54:30".to_string(),
105            process_id: "78".to_string(),
106            event_type: PostlogdEventType::ConfigOverrideWarning {
107                file_path: "/etc/postfix/main.cf".to_string(),
108                line_number: 826,
109                parameter: "smtpd_discard_ehlo_keywords".to_string(),
110                value: "silent-discard,dsn,etrn".to_string(),
111            },
112        };
113
114        let display = format!("{}", event);
115        assert!(display.contains("postlogd[78]"));
116        assert!(display.contains("Config override warning"));
117        assert!(display.contains("main.cf"));
118        assert!(display.contains("line 826"));
119    }
120
121    #[test]
122    fn test_complex_parameter_value() {
123        let event = PostlogdEvent {
124            timestamp: "Apr 08 17:54:30".to_string(),
125            process_id: "78".to_string(),
126            event_type: PostlogdEventType::ConfigOverrideWarning {
127                file_path: "/etc/postfix/main.cf".to_string(),
128                line_number: 820,
129                parameter: "smtpd_recipient_restrictions".to_string(),
130                value: "check_client_access pcre:/etc/postfix/filter_trusted, permit_sasl_authenticated, permit_mynetworks, reject_unauth_destination, pcre:/etc/postfix/filter_default".to_string(),
131            },
132        };
133
134        let PostlogdEventType::ConfigOverrideWarning { value, .. } = &event.event_type;
135        assert!(value.contains("check_client_access"));
136        assert!(value.contains("permit_sasl_authenticated"));
137        assert!(value.contains("reject_unauth_destination"));
138    }
139
140    #[test]
141    fn test_different_parameters() {
142        let events = vec![
143            (
144                "smtpd_recipient_restrictions",
145                "check_client_access pcre:/etc/postfix/filter_trusted",
146            ),
147            ("smtpd_client_message_rate_limit", "0"),
148            ("smtpd_discard_ehlo_keywords", "silent-discard,dsn,etrn"),
149        ];
150
151        for (param, val) in events {
152            let event = PostlogdEvent {
153                timestamp: "Apr 10 11:17:10".to_string(),
154                process_id: "78".to_string(),
155                event_type: PostlogdEventType::ConfigOverrideWarning {
156                    file_path: "/etc/postfix/main.cf".to_string(),
157                    line_number: 806,
158                    parameter: param.to_string(),
159                    value: val.to_string(),
160                },
161            };
162
163            let PostlogdEventType::ConfigOverrideWarning {
164                parameter, value, ..
165            } = &event.event_type;
166
167            assert_eq!(parameter, param);
168            assert_eq!(value, val);
169        }
170    }
171}