Browse Source

Allow sendmail to be configurable

This reverts a previous change which removed the sendmail to be configurable.
We now set the config to be read-only, and omit all read-only values from being stored during a save action from the admin interface.

Signed-off-by: BlackDex <black.dex@gmail.com>
pull/5438/head
BlackDex 3 months ago
parent
commit
9f66551289
No known key found for this signature in database GPG Key ID: 58C80A2AA6C765E1
  1. 2
      .env.template
  2. 2
      src/api/admin.rs
  3. 31
      src/config.rs
  4. 4
      src/mail.rs

2
.env.template

@ -534,6 +534,8 @@
# Whether to send mail via the `sendmail` command # Whether to send mail via the `sendmail` command
# USE_SENDMAIL=false # USE_SENDMAIL=false
# Which sendmail command to use. The one found in the $PATH is used if not specified.
# SENDMAIL_COMMAND="/path/to/sendmail"
## Defaults for SSL is "Plain" and "Login" and nothing for Non-SSL connections. ## Defaults for SSL is "Plain" and "Login" and nothing for Non-SSL connections.
## Possible values: ["Plain", "Login", "Xoauth2"]. ## Possible values: ["Plain", "Login", "Xoauth2"].

2
src/api/admin.rs

@ -747,7 +747,7 @@ fn get_diagnostics_http(code: u16, _token: AdminToken) -> EmptyResult {
#[post("/config", format = "application/json", data = "<data>")] #[post("/config", format = "application/json", data = "<data>")]
fn post_config(data: Json<ConfigBuilder>, _token: AdminToken) -> EmptyResult { fn post_config(data: Json<ConfigBuilder>, _token: AdminToken) -> EmptyResult {
let data: ConfigBuilder = data.into_inner(); let data: ConfigBuilder = data.into_inner();
if let Err(e) = CONFIG.update_config(data) { if let Err(e) = CONFIG.update_config(data, true) {
err!(format!("Unable to save config: {e:?}")) err!(format!("Unable to save config: {e:?}"))
} }
Ok(()) Ok(())

31
src/config.rs

@ -116,6 +116,14 @@ macro_rules! make_config {
serde_json::from_str(&config_str).map_err(Into::into) serde_json::from_str(&config_str).map_err(Into::into)
} }
fn clear_non_editable(&mut self) {
$($(
if !$editable {
self.$name = None;
}
)+)+
}
/// Merges the values of both builders into a new builder. /// Merges the values of both builders into a new builder.
/// If both have the same element, `other` wins. /// If both have the same element, `other` wins.
fn merge(&self, other: &Self, show_overrides: bool, overrides: &mut Vec<String>) -> Self { fn merge(&self, other: &Self, show_overrides: bool, overrides: &mut Vec<String>) -> Self {
@ -678,6 +686,8 @@ make_config! {
_enable_smtp: bool, true, def, true; _enable_smtp: bool, true, def, true;
/// Use Sendmail |> Whether to send mail via the `sendmail` command /// Use Sendmail |> Whether to send mail via the `sendmail` command
use_sendmail: bool, true, def, false; use_sendmail: bool, true, def, false;
/// Sendmail Command |> Which sendmail command to use. The one found in the $PATH is used if not specified.
sendmail_command: String, false, option;
/// Host /// Host
smtp_host: String, true, option; smtp_host: String, true, option;
/// DEPRECATED smtp_ssl |> DEPRECATED - Please use SMTP_SECURITY /// DEPRECATED smtp_ssl |> DEPRECATED - Please use SMTP_SECURITY
@ -890,12 +900,16 @@ fn validate_config(cfg: &ConfigItems) -> Result<(), Error> {
} }
if cfg.use_sendmail { if cfg.use_sendmail {
let command = format!("sendmail{EXE_SUFFIX}"); let command = cfg.sendmail_command.clone().unwrap_or_else(|| format!("sendmail{EXE_SUFFIX}"));
// Check if we can find the sendmail command to execute let mut path = std::path::PathBuf::from(&command);
let Ok(path) = which::which(&command) else { // Check if we can find the sendmail command to execute when no absolute path is given
if !path.is_absolute() {
let Ok(which_path) = which::which(&command) else {
err!(format!("sendmail command {command} not found in $PATH")) err!(format!("sendmail command {command} not found in $PATH"))
}; };
path = which_path;
}
match path.metadata() { match path.metadata() {
Err(err) if err.kind() == std::io::ErrorKind::NotFound => { Err(err) if err.kind() == std::io::ErrorKind::NotFound => {
@ -1142,12 +1156,17 @@ impl Config {
}) })
} }
pub fn update_config(&self, other: ConfigBuilder) -> Result<(), Error> { pub fn update_config(&self, other: ConfigBuilder, ignore_non_editable: bool) -> Result<(), Error> {
// Remove default values // Remove default values
//let builder = other.remove(&self.inner.read().unwrap()._env); //let builder = other.remove(&self.inner.read().unwrap()._env);
// TODO: Remove values that are defaults, above only checks those set by env and not the defaults // TODO: Remove values that are defaults, above only checks those set by env and not the defaults
let builder = other; let mut builder = other;
// Remove values that are not editable
if ignore_non_editable {
builder.clear_non_editable();
}
// Serialize now before we consume the builder // Serialize now before we consume the builder
let config_str = serde_json::to_string_pretty(&builder)?; let config_str = serde_json::to_string_pretty(&builder)?;
@ -1182,7 +1201,7 @@ impl Config {
let mut _overrides = Vec::new(); let mut _overrides = Vec::new();
usr.merge(&other, false, &mut _overrides) usr.merge(&other, false, &mut _overrides)
}; };
self.update_config(builder) self.update_config(builder, false)
} }
/// Tests whether an email's domain is allowed. A domain is allowed if it /// Tests whether an email's domain is allowed. A domain is allowed if it

4
src/mail.rs

@ -22,7 +22,11 @@ use crate::{
}; };
fn sendmail_transport() -> AsyncSendmailTransport<Tokio1Executor> { fn sendmail_transport() -> AsyncSendmailTransport<Tokio1Executor> {
if let Some(command) = CONFIG.sendmail_command() {
AsyncSendmailTransport::new_with_command(command)
} else {
AsyncSendmailTransport::new_with_command(format!("sendmail{EXE_SUFFIX}")) AsyncSendmailTransport::new_with_command(format!("sendmail{EXE_SUFFIX}"))
}
} }
fn smtp_transport() -> AsyncSmtpTransport<Tokio1Executor> { fn smtp_transport() -> AsyncSmtpTransport<Tokio1Executor> {

Loading…
Cancel
Save