Skip to content

Commit

Permalink
config_get() expression function + moved lookup.default.[hostname|dom…
Browse files Browse the repository at this point in the history
…ain] to server.hostname and report.domain
  • Loading branch information
mdecimus committed Jan 12, 2025
1 parent a4b1d5c commit a6c744b
Show file tree
Hide file tree
Showing 21 changed files with 167 additions and 107 deletions.
35 changes: 34 additions & 1 deletion crates/common/src/config/network.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,8 @@ use super::*;
#[derive(Clone)]
pub struct Network {
pub node_id: u64,
pub server_name: String,
pub report_domain: String,
pub security: Security,
pub contact_form: Option<ContactForm>,
pub http_response_url: IfBlock,
Expand Down Expand Up @@ -84,10 +86,12 @@ impl Default for Network {
http_response_url: IfBlock::new::<()>(
"server.http.url",
[],
"protocol + '://' + key_get('default', 'hostname') + ':' + local_port",
"protocol + '://' + config_get('server.hostname') + ':' + local_port",
),
http_allowed_endpoint: IfBlock::new::<()>("server.http.allowed-endpoint", [], "200"),
asn_geo_lookup: AsnGeoLookupConfig::Disabled,
server_name: Default::default(),
report_domain: Default::default(),
}
}
}
Expand Down Expand Up @@ -148,8 +152,37 @@ impl FieldOrDefault {

impl Network {
pub fn parse(config: &mut Config) -> Self {
let server_name = config
.value("server.hostname")
.map(|v| v.to_string())
.or_else(|| {
config
.value("lookup.default.hostname")
.map(|v| v.to_lowercase())
})
.unwrap_or_else(|| {
hostname::get()
.map(|v| v.to_string_lossy().to_lowercase())
.unwrap_or_else(|_| "localhost".to_string())
});
let report_domain = config
.value("report.domain")
.map(|v| v.to_lowercase())
.or_else(|| {
config
.value("lookup.default.domain")
.map(|v| v.to_lowercase())
})
.unwrap_or_else(|| {
psl::domain_str(&server_name)
.unwrap_or(server_name.as_str())
.to_string()
});

let mut network = Network {
node_id: config.property("cluster.node-id").unwrap_or_default(),
report_domain,
server_name,
security: Security::parse(config),
contact_form: ContactForm::parse(config),
asn_geo_lookup: AsnGeoLookupConfig::parse(config).unwrap_or_default(),
Expand Down
14 changes: 7 additions & 7 deletions crates/common/src/config/scripts.rs
Original file line number Diff line number Diff line change
Expand Up @@ -265,7 +265,7 @@ impl Scripting {

let hostname = config
.value("sieve.trusted.hostname")
.or_else(|| config.value("lookup.default.hostname"))
.or_else(|| config.value("server.hostname"))
.unwrap_or("localhost")
.to_string();
trusted_runtime.set_local_hostname(hostname.clone());
Expand Down Expand Up @@ -327,7 +327,7 @@ impl Scripting {
IfBlock::new::<()>(
"sieve.trusted.from-addr",
[],
"'MAILER-DAEMON@' + key_get('default', 'domain')",
"'MAILER-DAEMON@' + config_get('report.domain')",
)
}),
from_name: IfBlock::try_parse(config, "sieve.trusted.from-name", &token_map)
Expand All @@ -342,8 +342,8 @@ impl Scripting {
"sieve.trusted.sign",
[],
concat!(
"['rsa-' + key_get('default', 'domain'), ",
"'ed25519-' + key_get('default', 'domain')]"
"['rsa-' + config_get('report.domain'), ",
"'ed25519-' + config_get('report.domain')]"
),
)
},
Expand All @@ -363,16 +363,16 @@ impl Default for Scripting {
from_addr: IfBlock::new::<()>(
"sieve.trusted.from-addr",
[],
"'MAILER-DAEMON@' + key_get('default', 'domain')",
"'MAILER-DAEMON@' + config_get('report.domain')",
),
from_name: IfBlock::new::<()>("sieve.trusted.from-name", [], "'Mailer Daemon'"),
return_path: IfBlock::empty("sieve.trusted.return-path"),
sign: IfBlock::new::<()>(
"sieve.trusted.sign",
[],
concat!(
"['rsa-' + key_get('default', 'domain'), ",
"'ed25519-' + key_get('default', 'domain')]"
"['rsa-' + config_get('report.domain'), ",
"'ed25519-' + config_get('report.domain')]"
),
),
untrusted_scripts: AHashMap::new(),
Expand Down
2 changes: 1 addition & 1 deletion crates/common/src/config/smtp/auth.rs
Original file line number Diff line number Diff line change
Expand Up @@ -109,7 +109,7 @@ impl Default for MailAuthConfig {
seal: IfBlock::new::<()>(
"auth.arc.seal",
[],
"'rsa-' + key_get('default', 'domain')",
"'rsa-' + config_get('report.domain')",
),
},
spf: SpfAuthConfig {
Expand Down
10 changes: 3 additions & 7 deletions crates/common/src/config/smtp/queue.rs
Original file line number Diff line number Diff line change
Expand Up @@ -134,11 +134,7 @@ impl Default for QueueConfig {
),
notify: IfBlock::new::<()>("queue.schedule.notify", [], "[1d, 3d]"),
expire: IfBlock::new::<()>("queue.schedule.expire", [], "5d"),
hostname: IfBlock::new::<()>(
"queue.outbound.hostname",
[],
"key_get('default', 'hostname')",
),
hostname: IfBlock::new::<()>("queue.outbound.hostname", [], "config_get('server.hostname')"),
next_hop: IfBlock::new::<()>(
"queue.outbound.next-hop",
#[cfg(not(feature = "test_mode"))]
Expand Down Expand Up @@ -187,12 +183,12 @@ impl Default for QueueConfig {
address: IfBlock::new::<()>(
"report.dsn.from-address",
[],
"'MAILER-DAEMON@' + key_get('default', 'domain')",
"'MAILER-DAEMON@' + config_get('report.domain')",
),
sign: IfBlock::new::<()>(
"report.dsn.sign",
[],
"['rsa-' + key_get('default', 'domain'), 'ed25519-' + key_get('default', 'domain')]",
"['rsa-' + config_get('report.domain'), 'ed25519-' + config_get('report.domain')]",
),
},
timeout: QueueOutboundTimeout {
Expand Down
12 changes: 6 additions & 6 deletions crates/common/src/config/smtp/report.rs
Original file line number Diff line number Diff line change
Expand Up @@ -79,7 +79,7 @@ impl ReportConfig {
&TokenMap::default().with_variables(RCPT_DOMAIN_VARS),
)
.unwrap_or_else(|| {
IfBlock::new::<()>("report.submitter", [], "key_get('default', 'hostname')")
IfBlock::new::<()>("report.submitter", [], "config_get('server.hostname')")
}),
analysis: ReportAnalysis {
addresses: config
Expand Down Expand Up @@ -118,7 +118,7 @@ impl Report {
address: IfBlock::new::<()>(
format!("report.{id}.from-address"),
[],
format!("'noreply-{id}@' + key_get('default', 'domain')"),
format!("'noreply-{id}@' + config_get('report.domain')"),
),
subject: IfBlock::new::<()>(
format!("report.{id}.subject"),
Expand All @@ -131,7 +131,7 @@ impl Report {
sign: IfBlock::new::<()>(
format!("report.{id}.sign"),
[],
"['rsa-' + key_get('default', 'domain'), 'ed25519-' + key_get('default', 'domain')]",
"['rsa-' + config_get('report.domain'), 'ed25519-' + config_get('report.domain')]",
),
send: IfBlock::new::<()>(format!("report.{id}.send"), [], "[1, 1d]"),
};
Expand Down Expand Up @@ -164,12 +164,12 @@ impl AggregateReport {
address: IfBlock::new::<()>(
format!("report.{id}.aggregate.from-address"),
[],
format!("'noreply-{id}@' + key_get('default', 'domain')"),
format!("'noreply-{id}@' + config_get('report.domain')"),
),
org_name: IfBlock::new::<()>(
format!("report.{id}.aggregate.org-name"),
[],
"key_get('default', 'domain')",
"config_get('report.domain')",
),
contact_info: IfBlock::empty(format!("report.{id}.aggregate.contact-info")),
send: IfBlock::new::<AggregateFrequency>(
Expand All @@ -180,7 +180,7 @@ impl AggregateReport {
sign: IfBlock::new::<()>(
format!("report.{id}.aggregate.sign"),
[],
"['rsa-' + key_get('default', 'domain'), 'ed25519-' + key_get('default', 'domain')]",
"['rsa-' + config_get('report.domain'), 'ed25519-' + config_get('report.domain')]",
),
max_size: IfBlock::new::<()>(format!("report.{id}.aggregate.max-size"), [], "26214400"),
};
Expand Down
4 changes: 2 additions & 2 deletions crates/common/src/config/smtp/session.rs
Original file line number Diff line number Diff line change
Expand Up @@ -702,13 +702,13 @@ impl Default for SessionConfig {
hostname: IfBlock::new::<()>(
"server.connect.hostname",
[],
"key_get('default', 'hostname')",
"config_get('server.hostname')",
),
script: IfBlock::empty("session.connect.script"),
greeting: IfBlock::new::<()>(
"session.connect.greeting",
[],
"key_get('default', 'hostname') + ' Stalwart ESMTP at your service'",
"config_get('server.hostname') + ' Stalwart ESMTP at your service'",
),
},
ehlo: Ehlo {
Expand Down
4 changes: 3 additions & 1 deletion crates/common/src/enterprise/config.rs
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,9 @@ impl Enterprise {
stores: &Stores,
data: &Store,
) -> Option<Self> {
let server_hostname = config.value("lookup.default.hostname")?;
let server_hostname = config
.value("server.hostname")
.or_else(|| config.value("lookup.default.hostname"))?;
let mut update_license = None;

let license_result = match (
Expand Down
33 changes: 26 additions & 7 deletions crates/common/src/expr/eval.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14,12 +14,12 @@ use crate::Server;
use super::{
functions::{ResolveVariable, FUNCTIONS},
if_block::IfBlock,
BinaryOperator, Constant, Expression, ExpressionItem, UnaryOperator, Variable,
BinaryOperator, Constant, Expression, ExpressionItem, Setting, UnaryOperator, Variable,
};

impl Server {
pub async fn eval_if<'x, R: TryFrom<Variable<'x>>, V: ResolveVariable>(
&self,
&'x self,
if_block: &'x IfBlock,
resolver: &'x V,
session_id: u64,
Expand Down Expand Up @@ -81,7 +81,7 @@ impl Server {
}

pub async fn eval_expr<'x, R: TryFrom<Variable<'x>>, V: ResolveVariable>(
&self,
&'x self,
expr: &'x Expression,
resolver: &'x V,
expr_id: &str,
Expand Down Expand Up @@ -137,15 +137,15 @@ impl Server {
}
}

struct EvalContext<'x, 'y, V: ResolveVariable, T, C> {
struct EvalContext<'x, V: ResolveVariable, T, C> {
resolver: &'x V,
core: &'y Server,
core: &'x Server,
expr: &'x T,
captures: C,
session_id: u64,
}

impl<'x, V: ResolveVariable> EvalContext<'x, '_, V, IfBlock, Vec<String>> {
impl<'x, V: ResolveVariable> EvalContext<'x, V, IfBlock, Vec<String>> {
async fn eval(&mut self) -> trc::Result<Variable<'x>> {
for if_then in &self.expr.if_then {
if (EvalContext {
Expand Down Expand Up @@ -183,7 +183,7 @@ impl<'x, V: ResolveVariable> EvalContext<'x, '_, V, IfBlock, Vec<String>> {
}
}

impl<'x, V: ResolveVariable> EvalContext<'x, '_, V, Expression, &mut Vec<String>> {
impl<'x, V: ResolveVariable> EvalContext<'x, V, Expression, &mut Vec<String>> {
async fn eval(&mut self) -> trc::Result<Variable<'x>> {
let mut stack = Vec::new();
let mut exprs = self.expr.items.iter();
Expand All @@ -208,6 +208,25 @@ impl<'x, V: ResolveVariable> EvalContext<'x, '_, V, Expression, &mut Vec<String>
.to_string(),
)));
}
ExpressionItem::Setting(setting) => match setting {
Setting::Hostname => {
stack.push(self.core.core.network.server_name.as_str().into())
}
Setting::ReportDomain => {
stack.push(self.core.core.network.report_domain.as_str().into())
}
Setting::NodeId => stack.push(self.core.core.network.node_id.into()),
Setting::Other(key) => stack.push(
self.core
.core
.storage
.config
.get(key)
.await?
.unwrap_or_default()
.into(),
),
},
ExpressionItem::UnaryOperator(op) => {
let value = stack.pop().unwrap_or_default();
stack.push(match op {
Expand Down
21 changes: 21 additions & 0 deletions crates/common/src/expr/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -88,6 +88,7 @@ pub struct Expression {
pub enum ExpressionItem {
Variable(u32),
Global(String),
Setting(Setting),
Capture(u32),
Constant(Constant),
BinaryOperator(BinaryOperator),
Expand Down Expand Up @@ -200,6 +201,7 @@ pub enum Token {
num_args: u32,
},
Constant(Constant),
Setting(Setting),
Regex(Regex),
BinaryOperator(BinaryOperator),
UnaryOperator(UnaryOperator),
Expand All @@ -210,6 +212,25 @@ pub enum Token {
Comma,
}

#[derive(Debug, Clone)]
pub enum Setting {
Hostname,
ReportDomain,
NodeId,
Other(String),
}

impl From<String> for Setting {
fn from(value: String) -> Self {
match value.as_str() {
"server.hostname" => Setting::Hostname,
"report.domain" => Setting::ReportDomain,
"cluster.node-id" => Setting::NodeId,
_ => Setting::Other(value),
}
}
}

impl From<usize> for Variable<'_> {
fn from(value: usize) -> Self {
Variable::Integer(value as i64)
Expand Down
20 changes: 12 additions & 8 deletions crates/common/src/expr/parser.rs
Original file line number Diff line number Diff line change
Expand Up @@ -109,6 +109,16 @@ impl<'x> ExpressionParser<'x> {
self.output.push(ExpressionItem::Regex(regex.clone()));
self.operator_stack.pop();
}
Some((Token::Setting(setting), _)) => {
if self.arg_count.pop().unwrap() != 0 {
return Err(
"Expression function \"config_get\" expected 1 argument"
.to_string(),
);
}
self.output.push(ExpressionItem::Setting(setting.clone()));
self.operator_stack.pop();
}
_ => {}
}

Expand Down Expand Up @@ -156,16 +166,10 @@ impl<'x> ExpressionParser<'x> {
self.operator_stack
.push((Token::BinaryOperator(bop), jmp_pos));
}
Token::Function { id, name, num_args } => {
self.inc_arg_count();
self.arg_count.push(0);
self.operator_stack
.push((Token::Function { id, name, num_args }, None))
}
Token::Regex(regex) => {
token @ (Token::Function { .. } | Token::Regex(_) | Token::Setting(_)) => {
self.inc_arg_count();
self.arg_count.push(0);
self.operator_stack.push((Token::Regex(regex), None))
self.operator_stack.push((token, None))
}
Token::OpenBracket => {
// Array functions
Expand Down
7 changes: 7 additions & 0 deletions crates/common/src/expr/tokenizer.rs
Original file line number Diff line number Diff line change
Expand Up @@ -100,6 +100,13 @@ impl<'x> Tokenizer<'x> {
self.buf.clear();
self.find_char(b",")?;
(Token::Regex(regex).into(), b'(')
} else if ch == b'(' && self.buf.eq(b"config_get") {
// Parse setting
let stop_ch = self.find_char(b"\"'")?;
let setting_str = self.parse_string(stop_ch)?;
self.has_alpha = false;
self.buf.clear();
(Token::Setting(Setting::from(setting_str)).into(), b'(')
} else if !self.buf.is_empty() {
self.is_start = false;
(self.parse_buf()?.into(), ch)
Expand Down
Loading

0 comments on commit a6c744b

Please sign in to comment.