From a5ee03ed0f7949e91f11ec5f7ace57e337452c63 Mon Sep 17 00:00:00 2001 From: Lukas Wagner Date: Thu, 20 Jul 2023 16:31:59 +0200 Subject: [PATCH] notify: sendmail: support the `mailto-user` parameter This parameter allows to send mails to the email address configured for users from the product's user database. `proxmox-notify` now has a `Context` that must be set via `proxmox_notify::context::set_context` before the crate is used. Signed-off-by: Lukas Wagner --- pve-rs/Cargo.toml | 1 + pve-rs/src/lib.rs | 3 +- pve-rs/src/notify.rs | 82 +++++++++++++++++++++++++++++++++++++++++++- 3 files changed, 84 insertions(+), 2 deletions(-) diff --git a/pve-rs/Cargo.toml b/pve-rs/Cargo.toml index b28c118..954665f 100644 --- a/pve-rs/Cargo.toml +++ b/pve-rs/Cargo.toml @@ -23,6 +23,7 @@ env_logger = "0.9" hex = "0.4" http = "0.2.7" libc = "0.2" +log = "0.4.17" nix = "0.26" openssl = "0.10.40" serde = "1.0" diff --git a/pve-rs/src/lib.rs b/pve-rs/src/lib.rs index 0d63c28..49483d7 100644 --- a/pve-rs/src/lib.rs +++ b/pve-rs/src/lib.rs @@ -11,10 +11,11 @@ pub mod tfa; #[perlmod::package(name = "Proxmox::Lib::PVE", lib = "pve_rs")] mod export { - use crate::common; + use crate::{common, notify}; #[export] pub fn init() { common::logger::init("PVE_LOG", "info"); + notify::init(); } } diff --git a/pve-rs/src/notify.rs b/pve-rs/src/notify.rs index 08726e5..8def064 100644 --- a/pve-rs/src/notify.rs +++ b/pve-rs/src/notify.rs @@ -1,3 +1,79 @@ +use std::path::Path; + +use log; + +use proxmox_notify::context::Context; + +// Some helpers borrowed and slightly adapted from `proxmox-mail-forward` + +fn normalize_for_return(s: Option<&str>) -> Option { + match s?.trim() { + "" => None, + s => Some(s.to_string()), + } +} + +fn attempt_file_read>(path: P) -> Option { + match proxmox_sys::fs::file_read_optional_string(path) { + Ok(contents) => contents, + Err(err) => { + log::error!("{err}"); + None + } + } +} + +fn lookup_mail_address(content: &str, user: &str) -> Option { + normalize_for_return(content.lines().find_map(|line| { + let fields: Vec<&str> = line.split(':').collect(); + #[allow(clippy::get_first)] // to keep expression style consistent + match fields.get(0)?.trim() == "user" && fields.get(1)?.trim() == user { + true => fields.get(6).copied(), + false => None, + } + })) +} + +#[derive(Debug)] +struct PVEContext; + +impl Context for PVEContext { + fn lookup_email_for_user(&self, user: &str) -> Option { + let content = attempt_file_read("/etc/pve/user.cfg"); + content.and_then(|content| lookup_mail_address(&content, user)) + } +} + +#[cfg(test)] +mod tests { + use crate::notify::lookup_mail_address; + + const USER_CONFIG: &str = " +user:root@pam:1:0:::root@example.com::: +user:test@pve:1:0:::test@example.com::: +user:no-mail@pve:1:0:::::: + "; + + #[test] + fn test_parse_mail() { + assert_eq!( + lookup_mail_address(USER_CONFIG, "root@pam"), + Some("root@example.com".to_string()) + ); + assert_eq!( + lookup_mail_address(USER_CONFIG, "test@pve"), + Some("test@example.com".to_string()) + ); + assert_eq!(lookup_mail_address(USER_CONFIG, "no-mail@pve"), None); + } +} + +static CONTEXT: PVEContext = PVEContext; + +pub fn init() { + proxmox_notify::context::set_context(&CONTEXT) +} + #[perlmod::package(name = "PVE::RS::Notify")] mod export { use anyhow::{bail, Error}; @@ -204,7 +280,8 @@ mod export { fn add_sendmail_endpoint( #[try_from_ref] this: &NotificationConfig, name: String, - mailto: Vec, + mailto: Option>, + mailto_user: Option>, from_address: Option, author: Option, comment: Option, @@ -217,6 +294,7 @@ mod export { &SendmailConfig { name, mailto, + mailto_user, from_address, author, comment, @@ -231,6 +309,7 @@ mod export { #[try_from_ref] this: &NotificationConfig, name: &str, mailto: Option>, + mailto_user: Option>, from_address: Option, author: Option, comment: Option, @@ -248,6 +327,7 @@ mod export { name, &SendmailConfigUpdater { mailto, + mailto_user, from_address, author, comment, -- 2.39.5