[pbs-devel] [PATCH proxmox v2 3/4] sendmail: add mail-forwarder feature

Shannon Sterz s.sterz at proxmox.com
Mon Dec 2 13:58:58 CET 2024


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 at proxmox.com>
---
 proxmox-sendmail/Cargo.toml |  4 +++
 proxmox-sendmail/src/lib.rs | 57 +++++++++++++++++++++++++++++++++++++
 2 files changed, 61 insertions(+)

diff --git a/proxmox-sendmail/Cargo.toml b/proxmox-sendmail/Cargo.toml
index 790b324b..e04e2595 100644
--- a/proxmox-sendmail/Cargo.toml
+++ b/proxmox-sendmail/Cargo.toml
@@ -14,3 +14,7 @@ anyhow = { workspace = true }
 base64 = { workspace = true }
 percent-encoding = { workspace = true }
 proxmox-time = { workspace = true }
+
+[features]
+default = []
+mail-forwarder = []
diff --git a/proxmox-sendmail/src/lib.rs b/proxmox-sendmail/src/lib.rs
index a42bb0cd..e1ddc63d 100644
--- a/proxmox-sendmail/src/lib.rs
+++ b/proxmox-sendmail/src/lib.rs
@@ -287,6 +287,56 @@ impl<'a> Mail<'a> {
         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> {
         let file_boundary = format!("----_=_NextPart_001_{}", now);
         let html_boundary = format!("----_=_NextPart_002_{}", now);
@@ -432,6 +482,13 @@ mod test {
         assert!(result.is_err());
     }
 
+    #[test]
+    #[cfg(feature = "mail-forwarder")]
+    fn forwarding_without_recipients_fails() {
+        let result = Mail::forward(&[], "me at example.com", String::from("text").as_bytes(), None);
+        assert!(result.is_err());
+    }
+
     #[test]
     fn simple_ascii_text_mail() {
         let mail = Mail::new(
-- 
2.39.5





More information about the pbs-devel mailing list