Skip to content

Commit

Permalink
v0.3.9
Browse files Browse the repository at this point in the history
  • Loading branch information
mdecimus committed Oct 7, 2023
1 parent 4ae0ede commit a081209
Show file tree
Hide file tree
Showing 21 changed files with 181 additions and 64 deletions.
11 changes: 11 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,17 @@

All notable changes to this project will be documented in this file. This project adheres to [Semantic Versioning](http://semver.org/).

## [0.3.9] - 2023-10-07

## Added
- Support for reading environment variables from configuration file using the `!ENV_VAR_NAME` special keyword.

### Changed
- Querying directories from a Sieve script is now done using the `query()` method from `eval`. Your scripts will need to be updated, please refer to the [new syntax](https://stalw.art/docs/smtp/filter/sieve#directory-queries).

### Fixed
- IPrev lookups of IPv4 mapped to IPv6 addresses.

## [0.3.8] - 2023-09-19

## Added
Expand Down
40 changes: 20 additions & 20 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion crates/cli/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ authors = ["Stalwart Labs Ltd. <[email protected]>"]
license = "AGPL-3.0-only"
repository = "https://github.com/stalwartlabs/cli"
homepage = "https://github.com/stalwartlabs/cli"
version = "0.3.8"
version = "0.3.9"
edition = "2021"
readme = "README.md"
resolver = "2"
Expand Down
4 changes: 3 additions & 1 deletion crates/directory/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -186,7 +186,9 @@ impl Lookup {
match self {
Lookup::Directory { directory, query } => match directory.query(query, &[item]).await {
Ok(mut result) => match result.len() {
1 => result.pop().map(Variable::from).unwrap(),
1 if !matches!(result.first(), Some(QueryColumn::Null)) => {
result.pop().map(Variable::from).unwrap()
}
0 => Variable::default(),
_ => Variable::Array(result.into_iter().map(Variable::from).collect()),
}
Expand Down
2 changes: 1 addition & 1 deletion crates/imap/Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[package]
name = "imap"
version = "0.3.8"
version = "0.3.9"
edition = "2021"
resolver = "2"

Expand Down
2 changes: 1 addition & 1 deletion crates/install/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ authors = ["Stalwart Labs Ltd. <[email protected]>"]
license = "AGPL-3.0-only"
repository = "https://github.com/stalwartlabs/mail-server"
homepage = "https://github.com/stalwartlabs/mail-server"
version = "0.3.8"
version = "0.3.9"
edition = "2021"
readme = "README.md"
resolver = "2"
Expand Down
8 changes: 4 additions & 4 deletions crates/jmap/Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[package]
name = "jmap"
version = "0.3.8"
version = "0.3.9"
edition = "2021"
resolver = "2"

Expand Down Expand Up @@ -44,9 +44,9 @@ aes = "0.8.3"
cbc = { version = "0.1.2", features = ["alloc"] }
sequoia-openpgp = { version = "1.16", default-features = false, features = ["crypto-rust", "allow-experimental-crypto", "allow-variable-time-crypto"] }
rand = "0.8.5"
rasn = "0.9.5"
rasn-cms = "0.9.5"
rasn-pkix = "0.9.5"
rasn = "0.10"
rasn-cms = "0.10"
rasn-pkix = "0.10"
rsa = "0.9.2"
async-trait = "0.1.68"

Expand Down
2 changes: 1 addition & 1 deletion crates/main/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ homepage = "https://stalw.art"
keywords = ["imap", "jmap", "smtp", "email", "mail", "server"]
categories = ["email"]
license = "AGPL-3.0-only"
version = "0.3.8"
version = "0.3.9"
edition = "2021"
resolver = "2"

Expand Down
2 changes: 1 addition & 1 deletion crates/smtp/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ homepage = "https://stalw.art/smtp"
keywords = ["smtp", "email", "mail", "server"]
categories = ["email"]
license = "AGPL-3.0-only"
version = "0.3.8"
version = "0.3.9"
edition = "2021"
resolver = "2"

Expand Down
5 changes: 4 additions & 1 deletion crates/smtp/src/scripts/plugins/query.rs
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@
*/

use crate::config::scripts::SieveContext;
use directory::QueryColumn;
use sieve::{runtime::Variable, FunctionMap};

use super::PluginContext;
Expand Down Expand Up @@ -75,7 +76,9 @@ pub fn exec(ctx: PluginContext<'_>) -> Variable<'static> {
&parameters.iter().map(String::as_str).collect::<Vec<_>>(),
)) {
match query_columns.len() {
1 => query_columns.pop().map(Variable::from).unwrap(),
1 if !matches!(query_columns.first(), Some(QueryColumn::Null)) => {
query_columns.pop().map(Variable::from).unwrap()
}
0 => Variable::default(),
_ => Variable::Array(query_columns.into_iter().map(Variable::from).collect()),
}
Expand Down
5 changes: 4 additions & 1 deletion crates/utils/Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[package]
name = "utils"
version = "0.3.8"
version = "0.3.9"
edition = "2021"
resolver = "2"

Expand Down Expand Up @@ -29,3 +29,6 @@ tracing-journald = "0.3"

[features]
test_mode = []

[dev-dependencies]
tokio = { version = "1.23", features = ["full"] }
47 changes: 42 additions & 5 deletions crates/utils/src/config/parser.rs
Original file line number Diff line number Diff line change
Expand Up @@ -378,6 +378,35 @@ impl<'x> TomlParser<'x> {
}
}
}
'!' => {
let mut value = String::with_capacity(4);
while let Some(ch) = self.iter.peek() {
if ch.is_alphanumeric() || ['_', '-'].contains(ch) {
value.push(self.next_char(true, false)?);
} else {
break;
}
}
let value = match std::env::var(value.as_str()) {
Ok(value) => value,
Err(_) => {
tracing::warn!("Failed to get environment variable {value:?}");
String::new()
}
};
match self.keys.entry(key) {
Entry::Vacant(e) => {
e.insert(value);
}
Entry::Occupied(e) => {
return Err(format!(
"Duplicate key {:?} at line {}.",
e.key(),
self.line
));
}
}
}
ch => {
return if stop_chars.contains(&ch) {
Ok(ch)
Expand Down Expand Up @@ -421,11 +450,17 @@ mod tests {

#[test]
fn toml_parse() {
let mut file = PathBuf::from(env!("CARGO_MANIFEST_DIR"));
file.push("resources");
file.push("tests");
file.push("config");
file.push("toml-parser.toml");
let file = PathBuf::from(env!("CARGO_MANIFEST_DIR"))
.parent()
.unwrap()
.parent()
.unwrap()
.to_path_buf()
.join("tests")
.join("resources")
.join("smtp")
.join("config")
.join("toml-parser.toml");

let config = Config::parse(&fs::read_to_string(file).unwrap()).unwrap();
assert_eq!(
Expand Down Expand Up @@ -539,6 +574,8 @@ mod tests {
"strings.my \"string\" test.str3".to_string(),
"Name\tTabs\nNew Line.".to_string()
),
("env.var1".to_string(), "utils".to_string()),
("env.var2".to_string(), "utils".to_string()),
])
);
}
Expand Down
4 changes: 2 additions & 2 deletions resources/config/sieve/from.sieve
Original file line number Diff line number Diff line change
Expand Up @@ -145,13 +145,13 @@ if eval "!is_empty(envelope.from)" {
}

if eval "!t.FROM_SERVICE_ACCT &&
(contains_ignore_case(service_accounts, email_part(header.reply-to.addr, 'local')) ||
(contains_ignore_case(service_accounts, email_part(rto_addr, 'local')) ||
contains_ignore_case(service_accounts, email_part(header.sender.addr, 'local')))" {
let "t.FROM_SERVICE_ACCT" "1";
}

if eval "!t.WWW_DOT_DOMAIN &&
(contains_ignore_case(header.reply-to.addr, '@www.') ||
(contains_ignore_case(rto_addr, '@www.') ||
contains_ignore_case(header.sender.addr, '@www.'))" {
let "t.WWW_DOT_DOMAIN" "1";
}
Expand Down
3 changes: 3 additions & 0 deletions resources/config/sieve/prelude.sieve
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,9 @@ let "from_local" "email_part(from_addr, 'local')";
let "from_domain" "email_part(from_addr, 'domain')";
let "from_domain_sld" "domain_part(from_domain, 'sld')";

# Obtain Reply-To address
let "rto_addr" "to_lowercase(header.reply-to.addr)";

# Obtain Envelope From parts
let "envfrom_local" "email_part(envelope.from, 'local')";
let "envfrom_domain" "email_part(envelope.from, 'domain')";
Expand Down
6 changes: 3 additions & 3 deletions resources/config/sieve/rbl.sieve
Original file line number Diff line number Diff line change
Expand Up @@ -143,7 +143,7 @@ while "i < ip_addresses_len" {

# Query DNSWL
let "result" "rsplit_once(dns_query(ip_reverse + '.list.dnswl.org', 'ipv4')[0], '.')";
if eval "starts_with(result[0], '127.0.')" {
if eval "starts_with(result[0], '127.')" {
let "result" "result[1]";

if eval "result == 0" {
Expand All @@ -161,7 +161,7 @@ while "i < ip_addresses_len" {
}

# Validate domain names
let "emails" "dedup(winnow(to_lowercase([header.from, header.reply-to, envelope.from] + tokenize(text_body, 'email'))))";
let "emails" "dedup(winnow(to_lowercase([from_addr, rto_addr, envelope.from] + tokenize(text_body, 'email'))))";
let "emails_len" "count(emails)";
let "domains" "dedup(winnow(to_lowercase([ env.helo_domain, env.iprev.ptr ] + email_part(emails, 'domain') + puny_decode(uri_part(urls, 'host')))))";
let "domains_len" "count(domains)";
Expand Down Expand Up @@ -263,7 +263,7 @@ while "i > 0" {

# Query DNSWL
let "result" "rsplit_once(dns_query(env.dkim.domains[i] + '.dwl.dnswl.org', 'ipv4')[0], '.')";
if eval "result[0] == '127.0.0'" {
if eval "starts_with(result[0], '127.')" {
let "result" "result[1]";

if eval "result == 0" {
Expand Down
1 change: 0 additions & 1 deletion resources/config/sieve/replyto.sieve
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
let "rto_raw" "to_lowercase(header.reply-to.raw)";
if eval "!is_empty(rto_raw)" {
let "rto_addr" "to_lowercase(header.reply-to.addr)";
let "rto_name" "to_lowercase(header.reply-to.name)";

if eval "is_email(rto_addr)" {
Expand Down
Loading

0 comments on commit a081209

Please sign in to comment.