sendmail: add mail-forwarder feature

this moves the mail forwarding implementation from `proxmox-notify` into
`proxmox-sendmail` to cover more `sendmail` related use-cases in one
place.

Signed-off-by: Shannon Sterz <s.sterz@proxmox.com>
Reviewed-by: Lukas Wagner <l.wagner@proxmox.com>
Tested-by: Lukas Wagner <l.wagner@proxmox.com>
This commit is contained in:
Shannon Sterz 2024-12-02 15:16:54 +01:00 committed by Thomas Lamprecht
parent 57c84dbfb5
commit 043fec42f8
2 changed files with 61 additions and 0 deletions

View File

@ -14,3 +14,7 @@ anyhow = { workspace = true }
base64 = { workspace = true } base64 = { workspace = true }
percent-encoding = { workspace = true } percent-encoding = { workspace = true }
proxmox-time = { workspace = true } proxmox-time = { workspace = true }
[features]
default = []
mail-forwarder = []

View File

@ -291,6 +291,56 @@ impl<'a> Mail<'a> {
Ok(()) Ok(())
} }
/// Forwards an email message to a given list of recipients.
///
/// `message` must be compatible with ``sendmail`` (the message is piped into stdin unmodified).
#[cfg(feature = "mail-forwarder")]
pub fn forward(
mailto: &[&str],
mailfrom: &str,
message: &[u8],
uid: Option<u32>,
) -> Result<(), Error> {
use std::os::unix::process::CommandExt;
if mailto.is_empty() {
bail!("At least one recipient has to be specified!");
}
let mut builder = Command::new("/usr/sbin/sendmail");
builder
.args([
"-N", "never", // never send DSN (avoid mail loops)
"-f", mailfrom, "--",
])
.args(mailto)
.stdin(Stdio::piped())
.stdout(Stdio::null())
.stderr(Stdio::null());
if let Some(uid) = uid {
builder.uid(uid);
}
let mut sendmail_process = builder
.spawn()
.with_context(|| "could not spawn sendmail process")?;
sendmail_process
.stdin
.take()
.unwrap()
.write_all(message)
.with_context(|| "couldn't write to sendmail stdin")?;
sendmail_process
.wait()
.with_context(|| "sendmail did not exit successfully")?;
Ok(())
}
fn format_mail(&self, now: i64) -> Result<String, Error> { fn format_mail(&self, now: i64) -> Result<String, Error> {
use std::fmt::Write; use std::fmt::Write;
@ -442,6 +492,13 @@ mod test {
assert!(result.is_err()); assert!(result.is_err());
} }
#[test]
#[cfg(feature = "mail-forwarder")]
fn forwarding_without_recipients_fails() {
let result = Mail::forward(&[], "me@example.com", String::from("text").as_bytes(), None);
assert!(result.is_err());
}
#[test] #[test]
fn simple_ascii_text_mail() { fn simple_ascii_text_mail() {
let mail = Mail::new( let mail = Mail::new(