mirror of
https://github.com/stalwartlabs/smtp-server.git
synced 2024-11-24 06:19:41 +00:00
Github actions fixes.
This commit is contained in:
parent
f2f0d2e298
commit
415f98af15
9 changed files with 188 additions and 137 deletions
14
.github/workflows/build.yml
vendored
14
.github/workflows/build.yml
vendored
|
@ -59,14 +59,24 @@ jobs:
|
|||
run: echo "LIBCLANG_PATH=$((gcm clang).source -replace "clang.exe")" >> $env:GITHUB_ENV
|
||||
if: ${{ contains(matrix.host_os, 'windows') }}
|
||||
|
||||
- name: Install LLVM and Clang (Linux)
|
||||
- name: Install protoc, LLVM and Clang (Linux)
|
||||
if: ${{ contains(matrix.host_os, 'ubuntu') }}
|
||||
run: |
|
||||
sudo apt-get update -y
|
||||
sudo apt-get install protobuf-compiler
|
||||
sudo wget https://apt.llvm.org/llvm.sh
|
||||
sudo chmod +x llvm.sh
|
||||
sudo ./llvm.sh 14 all
|
||||
|
||||
- name: Install protoc (MacOs)
|
||||
if: ${{ contains(matrix.host_os, 'macos') }}
|
||||
run: |
|
||||
brew install protobuf
|
||||
|
||||
- name: Install protoc (Windows)
|
||||
if: ${{ contains(matrix.host_os, 'windows') }}
|
||||
uses: arduino/setup-protoc@v1
|
||||
|
||||
- name: Install ARM64 Linux build tools
|
||||
if: ${{ matrix.target == 'aarch64-unknown-linux-gnu' }}
|
||||
run: |
|
||||
|
@ -99,7 +109,7 @@ jobs:
|
|||
- name: XCode Version
|
||||
if: ${{ matrix.target == 'aarch64-apple-darwin' }}
|
||||
run: |
|
||||
sudo xcode-select -s /Applications/Xcode_12.4.app &&
|
||||
sudo xcode-select -s /Applications/Xcode_13.4.app &&
|
||||
sudo rm -Rf /Library/Developer/CommandLineTools/SDKs/*
|
||||
|
||||
- name: ARM64 Windows setup
|
||||
|
|
35
.github/workflows/test.yml
vendored
35
.github/workflows/test.yml
vendored
|
@ -38,6 +38,11 @@ jobs:
|
|||
- name: Checkout
|
||||
uses: actions/checkout@v1
|
||||
|
||||
- name: Install protobuf-compiler
|
||||
run: |
|
||||
sudo apt-get update -y
|
||||
sudo apt-get install protobuf-compiler
|
||||
|
||||
- name: Install Rust
|
||||
uses: actions-rs/toolchain@v1
|
||||
with:
|
||||
|
@ -51,33 +56,3 @@ jobs:
|
|||
command: test
|
||||
args: --all
|
||||
|
||||
- name: Database Tests
|
||||
uses: actions-rs/cargo@v1
|
||||
with:
|
||||
command: test
|
||||
args: store_tests --all
|
||||
|
||||
- name: JMAP Core Tests
|
||||
uses: actions-rs/cargo@v1
|
||||
with:
|
||||
command: test
|
||||
args: jmap_core_tests -- --ignored
|
||||
|
||||
- name: JMAP Mail Tests
|
||||
uses: actions-rs/cargo@v1
|
||||
with:
|
||||
command: test
|
||||
args: jmap_mail_tests -- --ignored
|
||||
|
||||
- name: Stress Tests
|
||||
uses: actions-rs/cargo@v1
|
||||
with:
|
||||
command: test
|
||||
args: jmap_stress_tests -- --ignored
|
||||
|
||||
- name: Cluster Tests
|
||||
uses: actions-rs/cargo@v1
|
||||
with:
|
||||
command: test
|
||||
args: cluster_tests -- --ignored
|
||||
|
||||
|
|
|
@ -49,6 +49,8 @@ reqwest = { version = "0.11", default-features = false, features = ["rustls-tls"
|
|||
serde = { version = "1.0", features = ["derive"] }
|
||||
serde_json = "1.0"
|
||||
num_cpus = "1.15.0"
|
||||
|
||||
[target.'cfg(unix)'.dependencies]
|
||||
privdrop = "0.5.3"
|
||||
|
||||
[dev-dependencies]
|
||||
|
|
|
@ -5,7 +5,8 @@ RUN apt-get update && \
|
|||
build-essential \
|
||||
cmake \
|
||||
clang \
|
||||
curl
|
||||
curl \
|
||||
protobuf-compiler
|
||||
ENV RUSTUP_HOME=/opt/rust/rustup \
|
||||
PATH=/home/root/.cargo/bin:/opt/rust/cargo/bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin
|
||||
RUN curl https://sh.rustup.rs -sSf | \
|
||||
|
@ -21,7 +22,6 @@ FROM chef AS planner
|
|||
COPY Cargo.toml .
|
||||
COPY Cargo.lock .
|
||||
COPY src/ src/
|
||||
COPY components/ components/
|
||||
COPY resources/ resources/
|
||||
RUN cargo chef prepare --recipe-path recipe.json
|
||||
|
||||
|
@ -31,7 +31,6 @@ RUN cargo chef cook --release --recipe-path recipe.json
|
|||
COPY Cargo.toml .
|
||||
COPY Cargo.lock .
|
||||
COPY src/ src/
|
||||
COPY components/ components/
|
||||
COPY resources/ resources/
|
||||
RUN cargo build --release
|
||||
|
||||
|
|
98
README.md
98
README.md
|
@ -7,7 +7,7 @@
|
|||
[![](https://img.shields.io/twitter/follow/stalwartlabs?style=flat)](https://twitter.com/stalwartlabs)
|
||||
|
||||
**Stalwart SMTP** is a modern SMTP server developed in Rust with a focus on security, speed, and extensive configurability.
|
||||
It features built-in DMARC, DKIM, SPF and ARC support for message and sender authentication, strong transport security through DANE, MTA-STS, SMTP TLS reporting and offers great flexibility and customization thanks to its configurable rules and native support for Sieve scripts.
|
||||
It features built-in DMARC, DKIM, SPF and ARC support for message authentication, strong transport security through DANE, MTA-STS and SMTP TLS reporting, and offers great flexibility and customization thanks to its dynamic configuration rules and native support for Sieve scripts.
|
||||
|
||||
Key features:
|
||||
|
||||
|
@ -24,17 +24,14 @@ Key features:
|
|||
- Inbound Filtering and Throttling:
|
||||
- Sieve scripting language with support for all [registered extensions](https://www.iana.org/assignments/sieve-extensions/sieve-extensions.xhtml).
|
||||
- Filtering, modification and removal of MIME parts or headers.
|
||||
- DNS block lists (**DNSBL**).
|
||||
- Greylisting.
|
||||
- Inbound concurrency and rate limiting.
|
||||
- DNS block lists (**DNSBL**) & Greylisting.
|
||||
- Inbound concurrency & rate limiting.
|
||||
- Integration with external content filtering systems such as SpamAssassin and ClamAV.
|
||||
- Flexible Queues:
|
||||
- Unlimited virtual queues.
|
||||
- Unlimited virtual queues with custom routing rules.
|
||||
- Delayed delivery with `FUTURERELEASE` and `DELIVERBY` extensions support.
|
||||
- Priority delivery with `MT-PRIORITY` extension support.
|
||||
- Quotas.
|
||||
- Outbound throttling.
|
||||
- Custom routing rules.
|
||||
- Outbound throttling & Disk quotas.
|
||||
- Logging and Reporting:
|
||||
- Detailed logging of SMTP transactions and events, including delivery attempts, errors, and policy violations.
|
||||
- Integration with **OpenTelemetry** to enable monitoring, tracing, and performance analysis of SMTP server operations.
|
||||
|
@ -60,7 +57,7 @@ You may also [compile Stalwart SMTP from the source](https://stalw.art/smtp/deve
|
|||
|
||||
If you are having problems running Stalwart SMTP, you found a bug or just have a question,
|
||||
do not hesitate to reach us on [Github Discussions](https://github.com/stalwartlabs/smtp-server/discussions),
|
||||
[Reddit](https://www.reddit.com/r/stalwartlabs) or [Discord](https://discord.gg/jtgtCNj66U).
|
||||
[Reddit](https://www.reddit.com/r/stalwartlabs) or [Discord](https://discord.gg/9dXkHzCk).
|
||||
Additionally you may become a sponsor to obtain priority support from Stalwart Labs Ltd.
|
||||
|
||||
## Documentation
|
||||
|
@ -72,45 +69,43 @@ Table of Contents
|
|||
- [Windows](https://stalw.art/smtp/get-started/windows/)
|
||||
- [Docker](https://stalw.art/smtp/get-started/docker/)
|
||||
- Configuration
|
||||
- Overview
|
||||
- General
|
||||
- Listeners
|
||||
- Rules
|
||||
- Scripting
|
||||
- Certificates
|
||||
- Remote
|
||||
- Databases
|
||||
- Lists
|
||||
- Resolver
|
||||
- Logging
|
||||
- Inbound
|
||||
- Overview
|
||||
- Connect
|
||||
- Extensions
|
||||
- Ehlo
|
||||
- Authentication
|
||||
- Mail-From
|
||||
- Data
|
||||
- Outbound
|
||||
- Overview
|
||||
- Schedule
|
||||
- Queue strategy
|
||||
- Throttling
|
||||
- Quotas
|
||||
- Authentication
|
||||
- Overview
|
||||
- Signatures
|
||||
- DKIM
|
||||
- SPF
|
||||
- ARC
|
||||
- DMARC
|
||||
- IpRev
|
||||
- DNSBL
|
||||
- [Overview](https://stalw.art/smtp/settings/overview)
|
||||
- [Configuration Rules](https://stalw.art/smtp/settings/rules)
|
||||
- [General settings](https://stalw.art/smtp/settings/general)
|
||||
- [Remote hosts](https://stalw.art/smtp/settings/remote)
|
||||
- [Databases](https://stalw.art/smtp/settings/database)
|
||||
- [Local Lists](https://stalw.art/smtp/settings/list)
|
||||
- [Tracing & Logging](https://stalw.art/smtp/settings/tracing)
|
||||
- Inbound settings
|
||||
- [Listeners](https://stalw.art/smtp/inbound/listeners)
|
||||
- [Sessions](https://stalw.art/smtp/inbound/session)
|
||||
- [EHLO Stage](https://stalw.art/smtp/inbound/ehlo)
|
||||
- [MAIL Stage](https://stalw.art/smtp/inbound/mail)
|
||||
- [RCPT Stage](https://stalw.art/smtp/inbound/rcpt)
|
||||
- [DATA Stage](https://stalw.art/smtp/inbound/data)
|
||||
- [AUTH Stage](https://stalw.art/smtp/inbound/auth)
|
||||
- [DNSBLs](https://stalw.art/smtp/inbound/dnsbl)
|
||||
- [Sieve Scripting](https://stalw.art/smtp/inbound/sieve)
|
||||
- [Throttling](https://stalw.art/smtp/inbound/throttle)
|
||||
- Outbound settings
|
||||
- [Queues](https://stalw.art/smtp/outbound/queue)
|
||||
- [Transport & Routing](https://stalw.art/smtp/outbound/transport)
|
||||
- [TLS Security](https://stalw.art/smtp/outbound/tls)
|
||||
- [Throttling](https://stalw.art/smtp/outbound/throttle)
|
||||
- [Quotas](https://stalw.art/smtp/outbound/quota)
|
||||
- [DNS](https://stalw.art/smtp/outbound/dns)
|
||||
- Email Authentication
|
||||
- [DKIM](https://stalw.art/smtp/auth/dkim)
|
||||
- [SPF](https://stalw.art/smtp/auth/spf)
|
||||
- [ARC](https://stalw.art/smtp/auth/arc)
|
||||
- [DMARC](https://stalw.art/smtp/auth/dmarc)
|
||||
- [Reverse IP](https://stalw.art/smtp/auth/iprev)
|
||||
- [Report Analysis](https://stalw.art/smtp/auth/analysis)
|
||||
- Management
|
||||
- Overview
|
||||
- Configuration
|
||||
- Queue
|
||||
- Reports
|
||||
- [API](https://stalw.art/smtp/management/api)
|
||||
- [CLI](https://stalw.art/smtp/management/cli)
|
||||
- [Queue](https://stalw.art/smtp/management/queue)
|
||||
- [Reports](https://stalw.art/smtp/management/reports)
|
||||
- Development
|
||||
- [Compiling](https://stalw.art/smtp/development/compile/)
|
||||
- [Tests](https://stalw.art/smtp/development/test/)
|
||||
|
@ -127,7 +122,8 @@ The following major features and enhancements are planned for Stalwart SMTP:
|
|||
|
||||
## Testing & Fuzzing
|
||||
|
||||
To run the test suite execute:
|
||||
The base tests perform protocol compliance tests as well as basic functionality testing on different functions across the Stalwart SMTP code base.
|
||||
To run the base test suite execute:
|
||||
|
||||
```bash
|
||||
cargo test
|
||||
|
@ -137,6 +133,12 @@ To run the fuzz tests please refer to the Stalwart libraries that handle parsing
|
|||
[mail-parser](https://github.com/stalwartlabs/mail-parser),
|
||||
[mail-auth](https://github.com/stalwartlabs/mail-auth) and [sieve-rs](https://github.com/stalwartlabs/sieve).
|
||||
|
||||
## Funding
|
||||
|
||||
Part of the development of this project was funded through the [NGI0 Entrust Fund](https://nlnet.nl/entrust), a fund established by [NLnet](https://nlnet.nl/) with financial support from the European Commission's [Next Generation Internet](https://ngi.eu/) programme, under the aegis of DG Communications Networks, Content and Technology under grant agreement No 101069594.
|
||||
|
||||
If you find the project useful you can help by [becoming a sponsor](https://github.com/sponsors/stalwartlabs). Thank you!
|
||||
|
||||
## License
|
||||
|
||||
Licensed under the terms of the [GNU Affero General Public License](https://www.gnu.org/licenses/agpl-3.0.en.html) as published by
|
||||
|
|
|
@ -230,7 +230,7 @@ data = "10m"
|
|||
mta-sts = "2m"
|
||||
|
||||
[[queue.quota]]
|
||||
#match = {if = "remote-ip", eq = "10.0.0.1"}
|
||||
#match = {if = "sender-domain", eq = "foobar.org"}
|
||||
#key = ["rcpt"]
|
||||
messages = 100000
|
||||
size = 10737418240 # 10gb
|
||||
|
@ -402,7 +402,55 @@ nested-includes = 5
|
|||
duplicate-expiry = "7d"
|
||||
|
||||
[sieve.scripts]
|
||||
#ehlo = ""
|
||||
# Note: These scripts are included here for demonstration purposes.
|
||||
# They should not be used in their current form.
|
||||
connect = '''
|
||||
require ["variables", "extlists", "reject"];
|
||||
|
||||
if string :list "${env.remote_ip}" "list/blocked-ips" {
|
||||
reject "Your IP '${env.remote_ip}' is not welcomed here.";
|
||||
}
|
||||
'''
|
||||
ehlo = '''
|
||||
require ["variables", "extlists", "reject"];
|
||||
|
||||
if string :list "${env.helo_domain}" "list/blocked-domains" {
|
||||
reject "551 5.1.1 Your domain '${env.helo_domain}' has been blacklisted.";
|
||||
}
|
||||
'''
|
||||
mail = '''
|
||||
require ["variables", "envelope", "reject"];
|
||||
|
||||
if envelope :localpart :is "from" "known_spammer" {
|
||||
reject "We do not accept SPAM.";
|
||||
}
|
||||
'''
|
||||
rcpt = '''
|
||||
require ["variables", "vnd.stalwart.execute", "envelope", "reject"];
|
||||
|
||||
set "triplet" "${env.remote_ip}.${envelope.from}.${envelope.to}";
|
||||
|
||||
if not execute :query "SELECT EXISTS(SELECT 1 FROM greylist WHERE addr=? LIMIT 1)" ["${triplet}"] {
|
||||
execute :query "INSERT INTO greylist (addr) VALUES (?)" ["${triplet}"];
|
||||
reject "422 4.2.2 Greylisted, please try again in a few moments.";
|
||||
}
|
||||
'''
|
||||
data = '''
|
||||
require ["envelope", "variables", "replace", "mime", "foreverypart", "editheader", "extracttext"];
|
||||
|
||||
if envelope :domain :is "to" "foobar.net" {
|
||||
set "counter" "a";
|
||||
foreverypart {
|
||||
if header :mime :contenttype "content-type" "text/html" {
|
||||
extracttext :upper "text_content";
|
||||
replace "${text_content}";
|
||||
}
|
||||
set :length "part_num" "${counter}";
|
||||
addheader :last "X-Part-Number" "${part_num}";
|
||||
set "counter" "${counter}a";
|
||||
}
|
||||
}
|
||||
'''
|
||||
|
||||
[management.lookup]
|
||||
auth = ["list/admin"]
|
||||
|
@ -410,6 +458,8 @@ auth = ["list/admin"]
|
|||
[list]
|
||||
domains = ["__DOMAIN__"]
|
||||
admin = ["admin:__ADMIN_PASS__"]
|
||||
#blocked-ips = ["10.0.0.1"]
|
||||
#blocked-domains = ["mail.spammer.com"]
|
||||
#users = "file:///usr/local/stalwart-smtp/etc/users.txt"
|
||||
|
||||
[certificate."default"]
|
||||
|
|
|
@ -29,8 +29,7 @@ use crate::config::StringMatch;
|
|||
|
||||
use super::{
|
||||
utils::{AsKey, ParseKey, ParseValue},
|
||||
Condition, ConditionMatch, Conditions, Config, ConfigContext, EnvelopeKey,
|
||||
IpAddrMask,
|
||||
Condition, ConditionMatch, Conditions, Config, ConfigContext, EnvelopeKey, IpAddrMask,
|
||||
};
|
||||
|
||||
impl Config {
|
||||
|
@ -58,14 +57,13 @@ impl Config {
|
|||
} else {
|
||||
return Err(format!(
|
||||
"Multiple operations found for condition {prefix:?}.",
|
||||
|
||||
));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if op_str.is_empty() {
|
||||
return Err(format!("Missing operation for condition {prefix:?}." ));
|
||||
return Err(format!("Missing operation for condition {prefix:?}."));
|
||||
} else if ["any-of", "all-of", "none-of"].contains(&op_str) {
|
||||
stack.push((
|
||||
std::mem::replace(
|
||||
|
@ -113,7 +111,6 @@ impl Config {
|
|||
if !available_keys.contains(&key) {
|
||||
return Err(format!(
|
||||
"Envelope key {key:?} is not available in this context for property {prefix:?}",
|
||||
|
||||
));
|
||||
}
|
||||
|
||||
|
@ -129,18 +126,16 @@ impl Config {
|
|||
"eq" | "equal-to" | "ne" | "not-equal-to" => {
|
||||
(MatchType::Equal, op_str == "ne" || op_str == "not-equal-to")
|
||||
}
|
||||
"in-list" | "not-in-list" => {
|
||||
(MatchType::Lookup, op_str == "not-in-list")
|
||||
"in-list" | "not-in-list" => (MatchType::Lookup, op_str == "not-in-list"),
|
||||
"matches" | "not-matches" => (MatchType::Regex, op_str.starts_with("not-")),
|
||||
"starts-with" | "not-starts-with" => {
|
||||
(MatchType::StartsWith, op_str == "not-starts-with")
|
||||
}
|
||||
"matches" | "not-matches" => {
|
||||
(MatchType::Regex, op_str.starts_with("not-"))
|
||||
"ends-with" | "not-ends-with" => {
|
||||
(MatchType::EndsWith, op_str == "not-ends-with")
|
||||
}
|
||||
"starts-with" | "not-starts-with" => (MatchType::StartsWith, op_str == "not-starts-with"),
|
||||
"ends-with" | "not-ends-with" => (MatchType::EndsWith, op_str == "not-ends-with"),
|
||||
_ => {
|
||||
return Err(format!(
|
||||
"Invalid operation {op_str:?} for key {prefix:?}."
|
||||
));
|
||||
return Err(format!("Invalid operation {op_str:?} for key {prefix:?}."));
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -180,33 +175,38 @@ impl Config {
|
|||
| EnvelopeKey::LocalIp
|
||||
| EnvelopeKey::RemoteIp,
|
||||
_,
|
||||
) => {
|
||||
match op {
|
||||
MatchType::Equal => ConditionMatch::String(StringMatch::Equal(value_str.to_string())),
|
||||
MatchType::StartsWith => ConditionMatch::String(StringMatch::StartsWith(value_str.to_string())),
|
||||
MatchType::EndsWith => ConditionMatch::String(StringMatch::EndsWith(value_str.to_string())),
|
||||
MatchType::Regex => ConditionMatch::Regex(Regex::new(value_str).map_err(|err| {
|
||||
) => match op {
|
||||
MatchType::Equal => {
|
||||
ConditionMatch::String(StringMatch::Equal(value_str.to_string()))
|
||||
}
|
||||
MatchType::StartsWith => {
|
||||
ConditionMatch::String(StringMatch::StartsWith(value_str.to_string()))
|
||||
}
|
||||
MatchType::EndsWith => {
|
||||
ConditionMatch::String(StringMatch::EndsWith(value_str.to_string()))
|
||||
}
|
||||
MatchType::Regex => {
|
||||
ConditionMatch::Regex(Regex::new(value_str).map_err(|err| {
|
||||
format!(
|
||||
"Failed to compile regular expression {:?} for key {:?}: {}.",
|
||||
value_str,
|
||||
(&prefix, value_str).as_key(),
|
||||
err
|
||||
)
|
||||
})?),
|
||||
MatchType::Lookup => {
|
||||
if let Some(list) = ctx.lookup.get(value_str) {
|
||||
ConditionMatch::Lookup(list.clone())
|
||||
} else {
|
||||
return Err(format!(
|
||||
"Lookup {:?} not found for property {:?}.",
|
||||
value_str,
|
||||
(&prefix, value_str).as_key()
|
||||
));
|
||||
}
|
||||
},
|
||||
})?)
|
||||
}
|
||||
|
||||
}
|
||||
MatchType::Lookup => {
|
||||
if let Some(list) = ctx.lookup.get(value_str) {
|
||||
ConditionMatch::Lookup(list.clone())
|
||||
} else {
|
||||
return Err(format!(
|
||||
"Lookup {:?} not found for property {:?}.",
|
||||
value_str,
|
||||
(&prefix, value_str).as_key()
|
||||
));
|
||||
}
|
||||
}
|
||||
},
|
||||
_ => {
|
||||
return Err(format!(
|
||||
"Invalid 'op'/'value' combination for key {:?}.",
|
||||
|
@ -347,10 +347,13 @@ mod tests {
|
|||
|
||||
use ahash::AHashMap;
|
||||
|
||||
use crate::{config::{
|
||||
Condition, ConditionMatch, Conditions, Config, ConfigContext, EnvelopeKey,
|
||||
IpAddrMask, Server, StringMatch,
|
||||
}, lookup::Lookup};
|
||||
use crate::{
|
||||
config::{
|
||||
Condition, ConditionMatch, Conditions, Config, ConfigContext, EnvelopeKey, IpAddrMask,
|
||||
Server, StringMatch,
|
||||
},
|
||||
lookup::Lookup,
|
||||
};
|
||||
|
||||
#[test]
|
||||
fn parse_conditions() {
|
||||
|
@ -397,7 +400,9 @@ mod tests {
|
|||
conditions: vec![
|
||||
Condition::Match {
|
||||
key: EnvelopeKey::SenderDomain,
|
||||
value: ConditionMatch::String(StringMatch::StartsWith("example".to_string())),
|
||||
value: ConditionMatch::String(StringMatch::StartsWith(
|
||||
"example".to_string(),
|
||||
)),
|
||||
not: false,
|
||||
},
|
||||
Condition::JumpIfFalse { positions: 1 },
|
||||
|
@ -415,7 +420,9 @@ mod tests {
|
|||
conditions: vec![
|
||||
Condition::Match {
|
||||
key: EnvelopeKey::RecipientDomain,
|
||||
value: ConditionMatch::String(StringMatch::Equal("example.org".to_string())),
|
||||
value: ConditionMatch::String(StringMatch::Equal(
|
||||
"example.org".to_string(),
|
||||
)),
|
||||
not: false,
|
||||
},
|
||||
Condition::JumpIfTrue { positions: 9 },
|
||||
|
@ -430,13 +437,17 @@ mod tests {
|
|||
Condition::JumpIfTrue { positions: 7 },
|
||||
Condition::Match {
|
||||
key: EnvelopeKey::Recipient,
|
||||
value: ConditionMatch::String(StringMatch::StartsWith("no-reply@".to_string())),
|
||||
value: ConditionMatch::String(StringMatch::StartsWith(
|
||||
"no-reply@".to_string(),
|
||||
)),
|
||||
not: false,
|
||||
},
|
||||
Condition::JumpIfFalse { positions: 5 },
|
||||
Condition::Match {
|
||||
key: EnvelopeKey::Sender,
|
||||
value: ConditionMatch::String(StringMatch::EndsWith("@domain.org".to_string())),
|
||||
value: ConditionMatch::String(StringMatch::EndsWith(
|
||||
"@domain.org".to_string(),
|
||||
)),
|
||||
not: false,
|
||||
},
|
||||
Condition::JumpIfFalse { positions: 3 },
|
||||
|
@ -457,7 +468,7 @@ mod tests {
|
|||
]);
|
||||
|
||||
for (key, rule) in expected_rules {
|
||||
assert_eq!(Some(rule), conditions.remove(&key), "failed for {key}" );
|
||||
assert_eq!(Some(rule), conditions.remove(&key), "failed for {key}");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -170,7 +170,7 @@ impl Config {
|
|||
.unwrap_or_else(|| IfBlock::new("Mail Delivery Subsystem".to_string())),
|
||||
address: self
|
||||
.parse_if_block("report.dsn.from-address", ctx, &sender_envelope_keys)?
|
||||
.unwrap_or_else(|| IfBlock::new(format!("MAILER-DAEMON@{default_hostname}" ))),
|
||||
.unwrap_or_else(|| IfBlock::new(format!("MAILER-DAEMON@{default_hostname}"))),
|
||||
sign: self
|
||||
.parse_if_block::<Vec<String>>("report.dsn.sign", ctx, &sender_envelope_keys)?
|
||||
.unwrap_or_default()
|
||||
|
@ -178,14 +178,14 @@ impl Config {
|
|||
},
|
||||
management_lookup: if let Some(lookup) = self.value("management.auth.lookup") {
|
||||
ctx.lookup
|
||||
.get(lookup)
|
||||
.ok_or_else(|| format!(
|
||||
"Lookup {lookup:?} not found for key \"management.auth.lookup\"."
|
||||
))?
|
||||
.clone()
|
||||
.get(lookup)
|
||||
.ok_or_else(|| {
|
||||
format!("Lookup {lookup:?} not found for key \"management.auth.lookup\".")
|
||||
})?
|
||||
.clone()
|
||||
} else {
|
||||
Arc::new(Lookup::default())
|
||||
}
|
||||
},
|
||||
};
|
||||
|
||||
if config.retry.has_empty_list() {
|
||||
|
@ -378,7 +378,6 @@ impl IfBlock<Option<String>> {
|
|||
.ok_or_else(|| {
|
||||
format!(
|
||||
"Host {then:?} not found for property \"queue.next-hop\".",
|
||||
|
||||
)
|
||||
})?
|
||||
.into(),
|
||||
|
|
|
@ -60,6 +60,9 @@ async fn main() -> std::io::Result<()> {
|
|||
config
|
||||
.parse_remote_hosts(&mut config_context)
|
||||
.failed("Configuration error");
|
||||
config
|
||||
.parse_databases(&mut config_context)
|
||||
.failed("Configuration error");
|
||||
config
|
||||
.parse_lists(&mut config_context)
|
||||
.failed("Configuration error");
|
||||
|
@ -262,7 +265,7 @@ async fn main() -> std::io::Result<()> {
|
|||
fn enable_tracing(config: &Config) -> stalwart_smtp::config::Result<Option<WorkerGuard>> {
|
||||
let level = config.value("global.tracing.level").unwrap_or("info");
|
||||
let env_filter = EnvFilter::builder()
|
||||
.parse(format!("smtp_server={}", level))
|
||||
.parse(format!("stalwart_smtp={}", level))
|
||||
.failed("Failed to log level");
|
||||
match config.value("global.tracing.method").unwrap_or_default() {
|
||||
"log" => {
|
||||
|
|
Loading…
Reference in a new issue