public inbox for pve-devel@lists.proxmox.com
 help / color / mirror / Atom feed
From: Lukas Wagner <l.wagner@proxmox.com>
To: pve-devel@lists.proxmox.com
Subject: [pve-devel] [PATCH proxmox 4/6] notify: move mail formatting to separate function
Date: Mon, 24 Jun 2024 14:31:32 +0200	[thread overview]
Message-ID: <20240624123134.321417-4-l.wagner@proxmox.com> (raw)
In-Reply-To: <20240624123134.321417-1-l.wagner@proxmox.com>

This way we can test this in a sane manner and refactor
safely.

Signed-off-by: Lukas Wagner <l.wagner@proxmox.com>
---
 proxmox-notify/src/endpoints/sendmail.rs | 109 +++++++++++++++++------
 1 file changed, 81 insertions(+), 28 deletions(-)

diff --git a/proxmox-notify/src/endpoints/sendmail.rs b/proxmox-notify/src/endpoints/sendmail.rs
index 0f7a61b0..241a2578 100644
--- a/proxmox-notify/src/endpoints/sendmail.rs
+++ b/proxmox-notify/src/endpoints/sendmail.rs
@@ -177,16 +177,13 @@ fn sendmail(
     mailfrom: &str,
     author: &str,
 ) -> Result<(), Error> {
-    use std::fmt::Write as _;
-
     if mailto.is_empty() {
         return Err(Error::Generic(
             "At least one recipient has to be specified!".into(),
         ));
     }
-    let recipients = mailto.join(",");
-
     let now = proxmox_time::epoch_i64();
+    let body = format_mail(mailto, mailfrom, author, subject, text, html, now)?;
 
     let mut sendmail_process = match Command::new("/usr/sbin/sendmail")
         .arg("-B")
@@ -205,13 +202,46 @@ fn sendmail(
         }
         Ok(process) => process,
     };
+
+    if let Err(err) = sendmail_process
+        .stdin
+        .take()
+        .unwrap()
+        .write_all(body.as_bytes())
+    {
+        return Err(Error::Generic(format!(
+            "couldn't write to sendmail stdin: {err}"
+        )));
+    };
+
+    // wait() closes stdin of the child
+    if let Err(err) = sendmail_process.wait() {
+        return Err(Error::Generic(format!(
+            "sendmail did not exit successfully: {err}"
+        )));
+    }
+
+    Ok(())
+}
+
+fn format_mail(
+    mailto: &[&str],
+    mailfrom: &str,
+    author: &str,
+    subject: &str,
+    text: Option<&str>,
+    html: Option<&str>,
+    timestamp: i64,
+) -> Result<String, Error> {
+    use std::fmt::Write as _;
+
+    let recipients = mailto.join(",");
     let mut is_multipart = false;
     if let (Some(_), Some(_)) = (text, html) {
         is_multipart = true;
     }
-
     let mut body = String::new();
-    let boundary = format!("----_=_NextPart_001_{}", now);
+    let boundary = format!("----_=_NextPart_001_{}", timestamp);
     if is_multipart {
         body.push_str("Content-Type: multipart/alternative;\n");
         let _ = writeln!(body, "\tboundary=\"{}\"", boundary);
@@ -226,11 +256,10 @@ fn sendmail(
     }
     let _ = writeln!(body, "From: {} <{}>", author, mailfrom);
     let _ = writeln!(body, "To: {}", &recipients);
-    let rfc2822_date = proxmox_time::epoch_to_rfc2822(now)
+    let rfc2822_date = proxmox_time::epoch_to_rfc2822(timestamp)
         .map_err(|err| Error::Generic(format!("failed to format time: {err}")))?;
     let _ = writeln!(body, "Date: {}", rfc2822_date);
     body.push_str("Auto-Submitted: auto-generated;\n");
-
     if is_multipart {
         body.push('\n');
         body.push_str("This is a multi-part message in MIME format.\n");
@@ -256,26 +285,7 @@ fn sendmail(
             let _ = write!(body, "\n--{}--", boundary);
         }
     }
-
-    if let Err(err) = sendmail_process
-        .stdin
-        .take()
-        .unwrap()
-        .write_all(body.as_bytes())
-    {
-        return Err(Error::Generic(format!(
-            "couldn't write to sendmail stdin: {err}"
-        )));
-    };
-
-    // wait() closes stdin of the child
-    if let Err(err) = sendmail_process.wait() {
-        return Err(Error::Generic(format!(
-            "sendmail did not exit successfully: {err}"
-        )));
-    }
-
-    Ok(())
+    Ok(body)
 }
 
 /// Forwards an email message to a given list of recipients.
@@ -342,4 +352,47 @@ mod test {
         );
         assert!(result.is_err());
     }
+
+    #[test]
+    fn test_format_mail_multipart() {
+        let message = format_mail(
+            &["Tony Est <test@example.com>"],
+            "foobar@example.com",
+            "Fred Oobar",
+            "This is the subject",
+            Some("This is the plain body"),
+            Some("<body>This is the HTML body</body>"),
+            1718977850,
+        )
+        .expect("format_message failed");
+
+        assert_eq!(
+            message,
+            r#"Content-Type: multipart/alternative;
+	boundary="----_=_NextPart_001_1718977850"
+MIME-Version: 1.0
+Subject: This is the subject
+From: Fred Oobar <foobar@example.com>
+To: Tony Est <test@example.com>
+Date: Fri, 21 Jun 2024 15:50:50 +0200
+Auto-Submitted: auto-generated;
+
+This is a multi-part message in MIME format.
+
+------_=_NextPart_001_1718977850
+Content-Type: text/plain;
+	charset="UTF-8"
+Content-Transfer-Encoding: 8bit
+
+This is the plain body
+------_=_NextPart_001_1718977850
+Content-Type: text/html;
+	charset="UTF-8"
+Content-Transfer-Encoding: 8bit
+
+<body>This is the HTML body</body>
+------_=_NextPart_001_1718977850--"#
+                .to_owned()
+        );
+    }
 }
-- 
2.39.2



_______________________________________________
pve-devel mailing list
pve-devel@lists.proxmox.com
https://lists.proxmox.com/cgi-bin/mailman/listinfo/pve-devel


  parent reply	other threads:[~2024-06-24 12:32 UTC|newest]

Thread overview: 7+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2024-06-24 12:31 [pve-devel] [PATCH proxmox 1/6] notify: copy sendmail/forward fn's from proxmox_sys Lukas Wagner
2024-06-24 12:31 ` [pve-devel] [PATCH proxmox 2/6] sys: mark email fn's as deprecated Lukas Wagner
2024-06-24 12:31 ` [pve-devel] [PATCH proxmox 3/6] notify: sendmail: make mailfrom and author non-optional Lukas Wagner
2024-06-24 12:31 ` Lukas Wagner [this message]
2024-06-24 12:31 ` [pve-devel] [PATCH proxmox 5/6] notify: sendmail: always send multi-part message Lukas Wagner
2024-06-24 12:31 ` [pve-devel] [PATCH proxmox 6/6] notify: sendmail: code style improvements Lukas Wagner
2024-07-12  8:56 ` [pve-devel] partially-applied: [PATCH proxmox 1/6] notify: copy sendmail/forward fn's from proxmox_sys Wolfgang Bumiller

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=20240624123134.321417-4-l.wagner@proxmox.com \
    --to=l.wagner@proxmox.com \
    --cc=pve-devel@lists.proxmox.com \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox
Service provided by Proxmox Server Solutions GmbH | Privacy | Legal