]> git.proxmox.com Git - proxmox.git/blame - proxmox-sys/src/email.rs
http: clippy fixups
[proxmox.git] / proxmox-sys / src / email.rs
CommitLineData
66004f22
HL
1//! Email related utilities.
2
bbc94222
WB
3use std::io::Write;
4use std::process::{Command, Stdio};
66004f22 5
336dab01
WB
6use anyhow::{bail, Error};
7
66004f22
HL
8/// Sends multi-part mail with text and/or html to a list of recipients
9///
10/// ``sendmail`` is used for sending the mail.
bbc94222 11pub fn sendmail(
48d049d4 12 mailto: &[&str],
bbc94222
WB
13 subject: &str,
14 text: Option<&str>,
15 html: Option<&str>,
16 mailfrom: Option<&str>,
17 author: Option<&str>,
18) -> Result<(), Error> {
66004f22
HL
19 if mailto.is_empty() {
20 bail!("At least one recipient has to be specified!")
21 }
66004f22 22 let mailfrom = mailfrom.unwrap_or("root");
66004f22
HL
23 let recipients = mailto.join(",");
24 let author = author.unwrap_or("Proxmox Backup Server");
25
336dab01 26 let now = proxmox_time::epoch_i64();
66004f22
HL
27
28 let mut sendmail_process = match Command::new("/usr/sbin/sendmail")
29 .arg("-B")
30 .arg("8BITMIME")
31 .arg("-f")
32 .arg(mailfrom)
33 .arg("--")
a7f40023 34 .args(mailto)
66004f22 35 .stdin(Stdio::piped())
bbc94222
WB
36 .spawn()
37 {
66004f22 38 Err(err) => bail!("could not spawn sendmail process: {}", err),
bbc94222 39 Ok(process) => process,
66004f22
HL
40 };
41 let mut is_multipart = false;
42 if let (Some(_), Some(_)) = (text, html) {
43 is_multipart = true;
44 }
45
46 let mut body = String::new();
557cce7a 47 let boundary = format!("----_=_NextPart_001_{}", now);
66004f22
HL
48 if is_multipart {
49 body.push_str("Content-Type: multipart/alternative;\n");
50 body.push_str(&format!("\tboundary=\"{}\"\n", boundary));
51 body.push_str("MIME-Version: 1.0\n");
52 } else if !subject.is_ascii() {
53 body.push_str("MIME-Version: 1.0\n");
54 }
55 if !subject.is_ascii() {
bbc94222
WB
56 body.push_str(&format!(
57 "Subject: =?utf-8?B?{}?=\n",
58 base64::encode(subject)
59 ));
66004f22
HL
60 } else {
61 body.push_str(&format!("Subject: {}\n", subject));
62 }
63 body.push_str(&format!("From: {} <{}>\n", author, mailfrom));
64 body.push_str(&format!("To: {}\n", &recipients));
336dab01
WB
65 let localtime = proxmox_time::localtime(now)?;
66 let rfc2822_date = proxmox_time::strftime("%a, %d %b %Y %T %z", &localtime)?;
317d1a78 67 body.push_str(&format!("Date: {}\n", rfc2822_date));
66004f22
HL
68 if is_multipart {
69 body.push('\n');
70 body.push_str("This is a multi-part message in MIME format.\n");
71 body.push_str(&format!("\n--{}\n", boundary));
72 }
73 if let Some(text) = text {
74 body.push_str("Content-Type: text/plain;\n");
75 body.push_str("\tcharset=\"UTF-8\"\n");
76 body.push_str("Content-Transfer-Encoding: 8bit\n");
77 body.push('\n');
78 body.push_str(text);
79 if is_multipart {
80 body.push_str(&format!("\n--{}\n", boundary));
81 }
82 }
83 if let Some(html) = html {
84 body.push_str("Content-Type: text/html;\n");
85 body.push_str("\tcharset=\"UTF-8\"\n");
86 body.push_str("Content-Transfer-Encoding: 8bit\n");
87 body.push('\n');
88 body.push_str(html);
89 if is_multipart {
90 body.push_str(&format!("\n--{}--", boundary));
91 }
92 }
93
bbc94222
WB
94 if let Err(err) = sendmail_process
95 .stdin
96 .take()
97 .unwrap()
98 .write_all(body.as_bytes())
99 {
66004f22
HL
100 bail!("couldn't write to sendmail stdin: {}", err)
101 };
102
103 // wait() closes stdin of the child
104 if let Err(err) = sendmail_process.wait() {
105 bail!("sendmail did not exit successfully: {}", err)
106 }
107
108 Ok(())
109}
110
111#[cfg(test)]
112mod test {
ec3965fd 113 use crate::email::sendmail;
66004f22 114
66004f22 115 #[test]
02acce2d 116 fn email_without_recipients() {
66004f22 117 let result = sendmail(
48d049d4 118 &[],
66004f22
HL
119 "Subject2",
120 None,
121 Some("<b>HTML</b>"),
122 None,
bbc94222
WB
123 Some("test1"),
124 );
66004f22
HL
125 assert!(result.is_err());
126 }
bbc94222 127}