[pve-devel] [PATCH v4 proxmox 07/69] notify: add sendmail plugin

Lukas Wagner l.wagner at proxmox.com
Thu Jul 20 16:31:34 CEST 2023


This plugin uses the 'sendmail' command to send an email
to one or more recipients.

Signed-off-by: Lukas Wagner <l.wagner at proxmox.com>
---
 proxmox-notify/Cargo.toml                |  9 ++-
 proxmox-notify/src/config.rs             | 12 ++++
 proxmox-notify/src/endpoints/mod.rs      |  2 +
 proxmox-notify/src/endpoints/sendmail.rs | 89 ++++++++++++++++++++++++
 proxmox-notify/src/lib.rs                | 18 +++++
 5 files changed, 128 insertions(+), 2 deletions(-)
 create mode 100644 proxmox-notify/src/endpoints/sendmail.rs

diff --git a/proxmox-notify/Cargo.toml b/proxmox-notify/Cargo.toml
index 2cd6278a..c19cae14 100644
--- a/proxmox-notify/Cargo.toml
+++ b/proxmox-notify/Cargo.toml
@@ -8,12 +8,17 @@ repository.workspace = true
 exclude.workspace = true
 
 [dependencies]
+handlebars = { workspace = true, optional = true }
 lazy_static.workspace = true
 log.workspace = true
 openssl.workspace = true
 proxmox-schema = { workspace = true, features = ["api-macro", "api-types"]}
 proxmox-section-config = { workspace = true }
-proxmox-sys.workspace = true
+proxmox-sys = { workspace = true, optional = true }
 regex.workspace = true
-serde.workspace = true
+serde = { workspace = true, features = ["derive"]}
 serde_json.workspace = true
+
+[features]
+default = ["sendmail"]
+sendmail = ["dep:handlebars", "dep:proxmox-sys"]
\ No newline at end of file
diff --git a/proxmox-notify/src/config.rs b/proxmox-notify/src/config.rs
index 362ca0fc..6269ec3c 100644
--- a/proxmox-notify/src/config.rs
+++ b/proxmox-notify/src/config.rs
@@ -13,6 +13,18 @@ lazy_static! {
 fn config_init() -> SectionConfig {
     let mut config = SectionConfig::new(&BACKEND_NAME_SCHEMA);
 
+    #[cfg(feature = "sendmail")]
+    {
+        use crate::endpoints::sendmail::{SendmailConfig, SENDMAIL_TYPENAME};
+
+        const SENDMAIL_SCHEMA: &ObjectSchema = SendmailConfig::API_SCHEMA.unwrap_object_schema();
+        config.register_plugin(SectionConfigPlugin::new(
+            SENDMAIL_TYPENAME.to_string(),
+            Some(String::from("name")),
+            SENDMAIL_SCHEMA,
+        ));
+    }
+
     config
 }
 
diff --git a/proxmox-notify/src/endpoints/mod.rs b/proxmox-notify/src/endpoints/mod.rs
index e69de29b..dd80d9bc 100644
--- a/proxmox-notify/src/endpoints/mod.rs
+++ b/proxmox-notify/src/endpoints/mod.rs
@@ -0,0 +1,2 @@
+#[cfg(feature = "sendmail")]
+pub mod sendmail;
diff --git a/proxmox-notify/src/endpoints/sendmail.rs b/proxmox-notify/src/endpoints/sendmail.rs
new file mode 100644
index 00000000..e9458cac
--- /dev/null
+++ b/proxmox-notify/src/endpoints/sendmail.rs
@@ -0,0 +1,89 @@
+use crate::schema::{EMAIL_SCHEMA, ENTITY_NAME_SCHEMA, USER_SCHEMA};
+use crate::{Endpoint, Error, Notification};
+
+use proxmox_schema::api_types::COMMENT_SCHEMA;
+use proxmox_schema::{api, Updater};
+use serde::{Deserialize, Serialize};
+
+pub(crate) const SENDMAIL_TYPENAME: &str = "sendmail";
+
+#[api(
+    properties: {
+        name: {
+            schema: ENTITY_NAME_SCHEMA,
+        },
+        mailto: {
+            type: Array,
+            items: {
+                schema: EMAIL_SCHEMA,
+            },
+        },
+        comment: {
+            optional: true,
+            schema: COMMENT_SCHEMA,
+        },
+    },
+)]
+#[derive(Debug, Serialize, Deserialize, Updater, Default)]
+#[serde(rename_all = "kebab-case")]
+/// Config for Sendmail notification endpoints
+pub struct SendmailConfig {
+    /// Name of the endpoint
+    #[updater(skip)]
+    pub name: String,
+    /// Mail recipients
+    pub mailto: Vec<String>,
+    /// `From` address for the mail
+    #[serde(skip_serializing_if = "Option::is_none")]
+    pub from_address: Option<String>,
+    /// Author of the mail
+    #[serde(skip_serializing_if = "Option::is_none")]
+    pub author: Option<String>,
+    /// Comment
+    #[serde(skip_serializing_if = "Option::is_none")]
+    pub comment: Option<String>,
+}
+
+#[derive(Serialize, Deserialize)]
+#[serde(rename_all = "kebab-case")]
+pub enum DeleteableSendmailProperty {
+    FromAddress,
+    Author,
+    Comment,
+}
+
+/// A sendmail notification endpoint.
+pub struct SendmailEndpoint {
+    pub config: SendmailConfig,
+}
+
+impl Endpoint for SendmailEndpoint {
+    fn send(&self, notification: &Notification) -> Result<(), Error> {
+        let recipients: Vec<&str> = self.config.mailto.iter().map(String::as_str).collect();
+
+        // Note: OX has serious problems displaying text mails,
+        // so we include html as well
+        let html = format!(
+            "<html><body><pre>\n{}\n<pre>",
+            handlebars::html_escape(&notification.body)
+        );
+
+        // proxmox_sys::email::sendmail will set the author to
+        // "Proxmox Backup Server" if it is not set.
+        let author = self.config.author.as_deref().or(Some(""));
+
+        proxmox_sys::email::sendmail(
+            &recipients,
+            &notification.title,
+            Some(&notification.body),
+            Some(&html),
+            self.config.from_address.as_deref(),
+            author,
+        )
+        .map_err(|err| Error::NotifyFailed(self.config.name.clone(), err.into()))
+    }
+
+    fn name(&self) -> &str {
+        &self.config.name
+    }
+}
diff --git a/proxmox-notify/src/lib.rs b/proxmox-notify/src/lib.rs
index f5bc687a..ff985393 100644
--- a/proxmox-notify/src/lib.rs
+++ b/proxmox-notify/src/lib.rs
@@ -200,6 +200,24 @@ impl Bus {
     pub fn from_config(config: &Config) -> Result<Self, Error> {
         let mut endpoints = HashMap::new();
 
+        // Instantiate endpoints
+
+        #[cfg(feature = "sendmail")]
+        {
+            use endpoints::sendmail::SENDMAIL_TYPENAME;
+            use endpoints::sendmail::{SendmailConfig, SendmailEndpoint};
+            endpoints.extend(
+                parse_endpoints_without_private_config!(
+                    config,
+                    SendmailConfig,
+                    SendmailEndpoint,
+                    SENDMAIL_TYPENAME
+                )?
+                .into_iter()
+                .map(|e| (e.name().into(), e)),
+            );
+        }
+
         Ok(Bus { endpoints })
     }
 
-- 
2.39.2






More information about the pve-devel mailing list