[pve-devel] [PATCH proxmox 5/6] notify: sendmail: always send multi-part message

Lukas Wagner l.wagner at proxmox.com
Mon Jun 24 14:31:33 CEST 2024


Even if we don't have an HTML template available, we always
send an HTML part (the plain text part wrapped in <pre>) to
improve rendering in certain mail clients. This means
we can simply message formatting, since we do not have to
distinguish between single-part and multi-part messages.

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

diff --git a/proxmox-notify/src/endpoints/sendmail.rs b/proxmox-notify/src/endpoints/sendmail.rs
index 241a2578..c28d9211 100644
--- a/proxmox-notify/src/endpoints/sendmail.rs
+++ b/proxmox-notify/src/endpoints/sendmail.rs
@@ -139,8 +139,8 @@ impl Endpoint for SendmailEndpoint {
                 sendmail(
                     &recipients_str,
                     &subject,
-                    Some(&text_part),
-                    Some(&html_part),
+                    &text_part,
+                    &html_part,
                     &mailfrom,
                     &author,
                 )
@@ -172,8 +172,8 @@ impl Endpoint for SendmailEndpoint {
 fn sendmail(
     mailto: &[&str],
     subject: &str,
-    text: Option<&str>,
-    html: Option<&str>,
+    text: &str,
+    html: &str,
     mailfrom: &str,
     author: &str,
 ) -> Result<(), Error> {
@@ -229,26 +229,20 @@ fn format_mail(
     mailfrom: &str,
     author: &str,
     subject: &str,
-    text: Option<&str>,
-    html: Option<&str>,
+    text: &str,
+    html: &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_{}", timestamp);
-    if is_multipart {
-        body.push_str("Content-Type: multipart/alternative;\n");
-        let _ = writeln!(body, "\tboundary=\"{}\"", boundary);
-        body.push_str("MIME-Version: 1.0\n");
-    } else if !subject.is_ascii() {
-        body.push_str("MIME-Version: 1.0\n");
-    }
+    body.push_str("Content-Type: multipart/alternative;\n");
+    let _ = writeln!(body, "\tboundary=\"{}\"", boundary);
+    body.push_str("MIME-Version: 1.0\n");
+
     if !subject.is_ascii() {
         let _ = writeln!(body, "Subject: =?utf-8?B?{}?=", base64::encode(subject));
     } else {
@@ -260,31 +254,21 @@ fn format_mail(
         .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");
-        let _ = write!(body, "\n--{}\n", boundary);
-    }
-    if let Some(text) = text {
-        body.push_str("Content-Type: text/plain;\n");
-        body.push_str("\tcharset=\"UTF-8\"\n");
-        body.push_str("Content-Transfer-Encoding: 8bit\n");
-        body.push('\n');
-        body.push_str(text);
-        if is_multipart {
-            let _ = write!(body, "\n--{}\n", boundary);
-        }
-    }
-    if let Some(html) = html {
-        body.push_str("Content-Type: text/html;\n");
-        body.push_str("\tcharset=\"UTF-8\"\n");
-        body.push_str("Content-Transfer-Encoding: 8bit\n");
-        body.push('\n');
-        body.push_str(html);
-        if is_multipart {
-            let _ = write!(body, "\n--{}--", boundary);
-        }
-    }
+    body.push('\n');
+    body.push_str("This is a multi-part message in MIME format.\n");
+    let _ = write!(body, "\n--{}\n", boundary);
+    body.push_str("Content-Type: text/plain;\n");
+    body.push_str("\tcharset=\"UTF-8\"\n");
+    body.push_str("Content-Transfer-Encoding: 8bit\n");
+    body.push('\n');
+    body.push_str(text);
+    let _ = write!(body, "\n--{}\n", boundary);
+    body.push_str("Content-Type: text/html;\n");
+    body.push_str("\tcharset=\"UTF-8\"\n");
+    body.push_str("Content-Transfer-Encoding: 8bit\n");
+    body.push('\n');
+    body.push_str(html);
+    let _ = write!(body, "\n--{}--", boundary);
     Ok(body)
 }
 
@@ -342,14 +326,7 @@ mod test {
 
     #[test]
     fn email_without_recipients() {
-        let result = sendmail(
-            &[],
-            "Subject2",
-            None,
-            Some("<b>HTML</b>"),
-            "root",
-            "Proxmox",
-        );
+        let result = sendmail(&[], "Subject2", "", "<b>HTML</b>", "root", "Proxmox");
         assert!(result.is_err());
     }
 
@@ -360,8 +337,8 @@ mod test {
             "foobar at example.com",
             "Fred Oobar",
             "This is the subject",
-            Some("This is the plain body"),
-            Some("<body>This is the HTML body</body>"),
+            "This is the plain body",
+            "<body>This is the HTML body</body>",
             1718977850,
         )
         .expect("format_message failed");
-- 
2.39.2





More information about the pve-devel mailing list