]>
Commit | Line | Data |
---|---|---|
c1a3505e | 1 | use serde::Deserialize; |
1516cc26 | 2 | use std::path::Path; |
c1a3505e LW |
3 | |
4 | use proxmox_schema::{ObjectSchema, Schema, StringSchema}; | |
5 | use proxmox_section_config::{SectionConfig, SectionConfigPlugin}; | |
6 | ||
7 | use crate::context::{common, Context}; | |
1516cc26 | 8 | use crate::Error; |
c1a3505e LW |
9 | |
10 | const PBS_USER_CFG_FILENAME: &str = "/etc/proxmox-backup/user.cfg"; | |
11 | const PBS_NODE_CFG_FILENAME: &str = "/etc/proxmox-backup/node.cfg"; | |
12 | ||
13 | // FIXME: Switch to the actual schema when possible in terms of dependency. | |
14 | // It's safe to assume that the config was written with the actual schema restrictions, so parsing | |
15 | // it with the less restrictive schema should be enough for the purpose of getting the mail address. | |
16 | const DUMMY_ID_SCHEMA: Schema = StringSchema::new("dummy ID").min_length(3).schema(); | |
17 | const DUMMY_EMAIL_SCHEMA: Schema = StringSchema::new("dummy email").schema(); | |
18 | const DUMMY_USER_SCHEMA: ObjectSchema = ObjectSchema { | |
19 | description: "minimal PBS user", | |
20 | properties: &[ | |
21 | ("userid", false, &DUMMY_ID_SCHEMA), | |
22 | ("email", true, &DUMMY_EMAIL_SCHEMA), | |
23 | ], | |
24 | additional_properties: true, | |
25 | default_key: None, | |
26 | }; | |
27 | ||
28 | #[derive(Deserialize)] | |
29 | struct DummyPbsUser { | |
30 | pub email: Option<String>, | |
31 | } | |
32 | ||
33 | /// Extract the root user's email address from the PBS user config. | |
34 | fn lookup_mail_address(content: &str, username: &str) -> Option<String> { | |
35 | let mut config = SectionConfig::new(&DUMMY_ID_SCHEMA).allow_unknown_sections(true); | |
36 | let user_plugin = SectionConfigPlugin::new( | |
37 | "user".to_string(), | |
38 | Some("userid".to_string()), | |
39 | &DUMMY_USER_SCHEMA, | |
40 | ); | |
41 | config.register_plugin(user_plugin); | |
42 | ||
43 | match config.parse(PBS_USER_CFG_FILENAME, content) { | |
44 | Ok(parsed) => { | |
45 | parsed.sections.get(username)?; | |
46 | match parsed.lookup::<DummyPbsUser>("user", username) { | |
47 | Ok(user) => common::normalize_for_return(user.email.as_deref()), | |
48 | Err(err) => { | |
49 | log::error!("unable to parse {PBS_USER_CFG_FILENAME}: {err}"); | |
50 | None | |
51 | } | |
52 | } | |
53 | } | |
54 | Err(err) => { | |
55 | log::error!("unable to parse {PBS_USER_CFG_FILENAME}: {err}"); | |
56 | None | |
57 | } | |
58 | } | |
59 | } | |
60 | ||
9bea76c6 LW |
61 | const DEFAULT_CONFIG: &str = "\ |
62 | sendmail: mail-to-root | |
63 | comment Send mails to root@pam's email address | |
64 | mailto-user root@pam | |
65 | ||
66 | ||
67 | matcher: default-matcher | |
68 | mode all | |
b2000d1f LW |
69 | invert-match true |
70 | match-field exact:type=prune | |
71 | match-severity info | |
9bea76c6 | 72 | target mail-to-root |
b2000d1f | 73 | comment Route everything but successful prune job notifications to mail-to-root |
9bea76c6 LW |
74 | "; |
75 | ||
c1a3505e LW |
76 | #[derive(Debug)] |
77 | pub struct PBSContext; | |
78 | ||
79 | pub static PBS_CONTEXT: PBSContext = PBSContext; | |
80 | ||
81 | impl Context for PBSContext { | |
82 | fn lookup_email_for_user(&self, user: &str) -> Option<String> { | |
83 | let content = common::attempt_file_read(PBS_USER_CFG_FILENAME); | |
84 | content.and_then(|content| lookup_mail_address(&content, user)) | |
85 | } | |
86 | ||
87 | fn default_sendmail_author(&self) -> String { | |
803bf7cd | 88 | format!("Proxmox Backup Server - {}", proxmox_sys::nodename()) |
c1a3505e LW |
89 | } |
90 | ||
91 | fn default_sendmail_from(&self) -> String { | |
92 | let content = common::attempt_file_read(PBS_NODE_CFG_FILENAME); | |
93 | content | |
94 | .and_then(|content| common::lookup_datacenter_config_key(&content, "email-from")) | |
95 | .unwrap_or_else(|| String::from("root")) | |
96 | } | |
97 | ||
98 | fn http_proxy_config(&self) -> Option<String> { | |
99 | let content = common::attempt_file_read(PBS_NODE_CFG_FILENAME); | |
100 | content.and_then(|content| common::lookup_datacenter_config_key(&content, "http-proxy")) | |
101 | } | |
9bea76c6 LW |
102 | |
103 | fn default_config(&self) -> &'static str { | |
104 | return DEFAULT_CONFIG; | |
105 | } | |
1516cc26 LW |
106 | |
107 | fn lookup_template( | |
108 | &self, | |
109 | filename: &str, | |
110 | namespace: Option<&str>, | |
111 | ) -> Result<Option<String>, Error> { | |
112 | let path = Path::new("/usr/share/proxmox-backup/templates") | |
113 | .join(namespace.unwrap_or("default")) | |
114 | .join(filename); | |
115 | ||
116 | let template_string = proxmox_sys::fs::file_read_optional_string(path) | |
117 | .map_err(|err| Error::Generic(format!("could not load template: {err}")))?; | |
118 | Ok(template_string) | |
119 | } | |
c1a3505e LW |
120 | } |
121 | ||
122 | #[cfg(test)] | |
123 | mod tests { | |
124 | use super::*; | |
125 | ||
126 | const USER_CONFIG: &str = " | |
127 | user: root@pam | |
128 | email root@example.com | |
129 | ||
130 | user: test@pbs | |
131 | enable true | |
132 | expire 0 | |
133 | "; | |
134 | ||
135 | #[test] | |
136 | fn test_parse_mail() { | |
137 | assert_eq!( | |
138 | lookup_mail_address(USER_CONFIG, "root@pam"), | |
139 | Some("root@example.com".to_string()) | |
140 | ); | |
141 | assert_eq!(lookup_mail_address(USER_CONFIG, "test@pbs"), None); | |
142 | } | |
143 | ||
144 | const NODE_CONFIG: &str = " | |
145 | default-lang: de | |
146 | email-from: root@example.com | |
147 | http-proxy: http://localhost:1234 | |
148 | "; | |
149 | ||
150 | #[test] | |
151 | fn test_parse_node_config() { | |
152 | assert_eq!( | |
153 | common::lookup_datacenter_config_key(NODE_CONFIG, "email-from"), | |
154 | Some("root@example.com".to_string()) | |
155 | ); | |
156 | assert_eq!( | |
157 | common::lookup_datacenter_config_key(NODE_CONFIG, "http-proxy"), | |
158 | Some("http://localhost:1234".to_string()) | |
159 | ); | |
160 | assert_eq!( | |
161 | common::lookup_datacenter_config_key(NODE_CONFIG, "foo"), | |
162 | None | |
163 | ); | |
164 | } | |
165 | } |