aboutsummaryrefslogtreecommitdiffstats
path: root/pkgs/by-name/st
diff options
context:
space:
mode:
Diffstat (limited to 'pkgs/by-name/st')
-rw-r--r--pkgs/by-name/st/stalwart-mail-patched/mail-send.nix20
-rw-r--r--pkgs/by-name/st/stalwart-mail-patched/package.nix12
-rw-r--r--pkgs/by-name/st/stalwart-mail-patched/patches/crates-Use-the-platform-CA-bundle-instead-of-the-bun.patch879
-rw-r--r--pkgs/by-name/st/stalwart-mail-patched/patches/use-platform-ca-roots.patch747
4 files changed, 749 insertions, 909 deletions
diff --git a/pkgs/by-name/st/stalwart-mail-patched/mail-send.nix b/pkgs/by-name/st/stalwart-mail-patched/mail-send.nix
deleted file mode 100644
index e0d8c57..0000000
--- a/pkgs/by-name/st/stalwart-mail-patched/mail-send.nix
+++ /dev/null
@@ -1,20 +0,0 @@
-{
- stdenv,
- fetchFromGitHub,
-}:
-stdenv.mkDerivation (finalAttrs: {
- pname = "mail-send";
- version = "0.5.0";
-
- src = fetchFromGitHub {
- owner = "stalwartlabs";
- repo = "mail-send";
- tag = "v${finalAttrs.version}";
- hash = "sha256-uDD4GLwjRpNqjtXPMask0twGW2Gcm1PFyDGXcfPS0F4=";
- };
-
- installPhase = ''
- mkdir --parents "$out"
- cp --recursive ./. "$out/"
- '';
-})
diff --git a/pkgs/by-name/st/stalwart-mail-patched/package.nix b/pkgs/by-name/st/stalwart-mail-patched/package.nix
index 5b1c409..9058f2c 100644
--- a/pkgs/by-name/st/stalwart-mail-patched/package.nix
+++ b/pkgs/by-name/st/stalwart-mail-patched/package.nix
@@ -4,8 +4,6 @@
nixLib,
}: let
spamfilter = callPackage ./spam-filter.nix {};
-
- mail-send = callPackage ./mail-send.nix {};
in
stalwart-mail.overrideAttrs (final: prev: {
pname = "stalwart-mail-patched";
@@ -28,21 +26,15 @@ in
# "elastic"
# "s3"
"redis"
+ "tls-native-roots"
];
- postUnpack =
- (prev.postUnpack or "")
- + ''
- cp --recursive "${mail-send}" ./source/crates/mail-send
- chmod --recursive +w "./source/crates/mail-send"
- '';
-
cargoPatches =
(prev.cargoPatches or [])
++ [
# `stalwart-mail` uses their bundled store, which makes it impossible to use our
# own CA certificate (e.g., for tests). Thus use a native version.
- ./patches/crates-Use-the-platform-CA-bundle-instead-of-the-bun.patch
+ ./patches/use-platform-ca-roots.patch
];
# Check that the enterprise feature is really disabled.
diff --git a/pkgs/by-name/st/stalwart-mail-patched/patches/crates-Use-the-platform-CA-bundle-instead-of-the-bun.patch b/pkgs/by-name/st/stalwart-mail-patched/patches/crates-Use-the-platform-CA-bundle-instead-of-the-bun.patch
deleted file mode 100644
index e6c3d4b..0000000
--- a/pkgs/by-name/st/stalwart-mail-patched/patches/crates-Use-the-platform-CA-bundle-instead-of-the-bun.patch
+++ /dev/null
@@ -1,879 +0,0 @@
-From 6825a35213d604a7149265af2346a69143c0853b Mon Sep 17 00:00:00 2001
-From: Benedikt Peetz <benedikt.peetz@b-peetz.de>
-Date: Tue, 4 Mar 2025 19:15:06 +0100
-Subject: [PATCH] crates/*: Use the platform CA bundle instead of the
- bundled certificates
-
----
- Cargo.lock | 284 ++++++++++++++++++++++++++++++-
- crates/cli/Cargo.toml | 2 +-
- crates/common/Cargo.toml | 4 +-
- crates/directory/Cargo.toml | 4 +-
- crates/imap/Cargo.toml | 2 +-
- crates/jmap/Cargo.toml | 4 +-
- crates/mail-send/Cargo.toml | 1 +
- crates/mail-send/src/smtp/tls.rs | 22 +--
- crates/managesieve/Cargo.toml | 2 +-
- crates/pop3/Cargo.toml | 2 +-
- crates/smtp/Cargo.toml | 4 +-
- crates/spam-filter/Cargo.toml | 4 +-
- crates/store/Cargo.toml | 2 +-
- crates/trc/Cargo.toml | 2 +-
- crates/utils/Cargo.toml | 5 +-
- crates/utils/src/lib.rs | 16 +-
- tests/Cargo.toml | 10 +-
- 17 files changed, 314 insertions(+), 56 deletions(-)
-
-diff --git a/Cargo.lock b/Cargo.lock
-index be36759b..eca9699f 100644
---- a/Cargo.lock
-+++ b/Cargo.lock
-@@ -440,6 +440,47 @@ dependencies = [
- "url",
- ]
-
-+[[package]]
-+name = "aws-lc-fips-sys"
-+version = "0.13.3"
-+source = "registry+https://github.com/rust-lang/crates.io-index"
-+checksum = "29003a681b2b9465c1139bfb726da452a841a8b025f35953f3bce71139f10b21"
-+dependencies = [
-+ "bindgen 0.69.5",
-+ "cc",
-+ "cmake",
-+ "dunce",
-+ "fs_extra",
-+ "paste",
-+ "regex",
-+]
-+
-+[[package]]
-+name = "aws-lc-rs"
-+version = "1.12.5"
-+source = "registry+https://github.com/rust-lang/crates.io-index"
-+checksum = "5e4e8200b9a4a5801a769d50eeabc05670fec7e959a8cb7a63a93e4e519942ae"
-+dependencies = [
-+ "aws-lc-fips-sys",
-+ "aws-lc-sys",
-+ "paste",
-+ "zeroize",
-+]
-+
-+[[package]]
-+name = "aws-lc-sys"
-+version = "0.26.0"
-+source = "registry+https://github.com/rust-lang/crates.io-index"
-+checksum = "0f9dd2e03ee80ca2822dd6ea431163d2ef259f2066a4d6ccaca6d9dcb386aa43"
-+dependencies = [
-+ "bindgen 0.69.5",
-+ "cc",
-+ "cmake",
-+ "dunce",
-+ "fs_extra",
-+ "paste",
-+]
-+
- [[package]]
- name = "aws-region"
- version = "0.25.5"
-@@ -673,12 +714,15 @@ dependencies = [
- "itertools 0.12.1",
- "lazy_static",
- "lazycell",
-+ "log",
-+ "prettyplease",
- "proc-macro2",
- "quote",
- "regex",
- "rustc-hash 1.1.0",
- "shlex",
- "syn 2.0.96",
-+ "which",
- ]
-
- [[package]]
-@@ -1035,6 +1079,12 @@ dependencies = [
- "smallvec",
- ]
-
-+[[package]]
-+name = "cesu8"
-+version = "1.1.0"
-+source = "registry+https://github.com/rust-lang/crates.io-index"
-+checksum = "6d43a04d8753f35258c91f8ec639f792891f748a1edbd759cf1dcea3382ad83c"
-+
- [[package]]
- name = "cexpr"
- version = "0.6.0"
-@@ -1347,6 +1397,16 @@ dependencies = [
- "libc",
- ]
-
-+[[package]]
-+name = "core-foundation"
-+version = "0.10.0"
-+source = "registry+https://github.com/rust-lang/crates.io-index"
-+checksum = "b55271e5c8c478ad3f38ad24ef34923091e0548492a266d19b3c0b4d82574c63"
-+dependencies = [
-+ "core-foundation-sys",
-+ "libc",
-+]
-+
- [[package]]
- name = "core-foundation-sys"
- version = "0.8.7"
-@@ -1912,6 +1972,12 @@ dependencies = [
- "zeroize",
- ]
-
-+[[package]]
-+name = "dunce"
-+version = "1.0.5"
-+source = "registry+https://github.com/rust-lang/crates.io-index"
-+checksum = "92773504d58c093f6de2459af4af33faa518c13451eb8f2b5698ed3d36e7c813"
-+
- [[package]]
- name = "dyn-clone"
- version = "1.0.17"
-@@ -2117,6 +2183,29 @@ dependencies = [
- "syn 2.0.96",
- ]
-
-+[[package]]
-+name = "env_filter"
-+version = "0.1.3"
-+source = "registry+https://github.com/rust-lang/crates.io-index"
-+checksum = "186e05a59d4c50738528153b83b0b0194d3a29507dfec16eccd4b342903397d0"
-+dependencies = [
-+ "log",
-+ "regex",
-+]
-+
-+[[package]]
-+name = "env_logger"
-+version = "0.11.6"
-+source = "registry+https://github.com/rust-lang/crates.io-index"
-+checksum = "dcaee3d8e3cfc3fd92428d477bc97fc29ec8716d180c0d74c643bb26166660e0"
-+dependencies = [
-+ "anstream",
-+ "anstyle",
-+ "env_filter",
-+ "humantime",
-+ "log",
-+]
-+
- [[package]]
- name = "equivalent"
- version = "1.0.1"
-@@ -2423,6 +2512,12 @@ dependencies = [
- "syn 2.0.96",
- ]
-
-+[[package]]
-+name = "fs_extra"
-+version = "1.3.0"
-+source = "registry+https://github.com/rust-lang/crates.io-index"
-+checksum = "42703706b716c37f96a77aea830392ad231f44c9e9a67872fa5548707e11b11c"
-+
- [[package]]
- name = "funty"
- version = "2.0.0"
-@@ -2974,6 +3069,12 @@ version = "0.4.3"
- source = "registry+https://github.com/rust-lang/crates.io-index"
- checksum = "9994b79e8c1a39b3166c63ae7823bb2b00831e2a96a31399c50fe69df408eaeb"
-
-+[[package]]
-+name = "humantime"
-+version = "2.1.0"
-+source = "registry+https://github.com/rust-lang/crates.io-index"
-+checksum = "9a3a5bfb195931eeb336b2a7b4d761daec841b97f947d34394601737a7bba5e4"
-+
- [[package]]
- name = "hyper"
- version = "0.14.32"
-@@ -3044,6 +3145,7 @@ dependencies = [
- "hyper 1.6.0",
- "hyper-util",
- "rustls 0.23.21",
-+ "rustls-native-certs 0.8.1",
- "rustls-pki-types",
- "tokio",
- "tokio-rustls 0.26.1",
-@@ -3607,6 +3709,28 @@ dependencies = [
- "utils",
- ]
-
-+[[package]]
-+name = "jni"
-+version = "0.21.1"
-+source = "registry+https://github.com/rust-lang/crates.io-index"
-+checksum = "1a87aa2bb7d2af34197c04845522473242e1aa17c12f4935d5856491a7fb8c97"
-+dependencies = [
-+ "cesu8",
-+ "cfg-if",
-+ "combine",
-+ "jni-sys",
-+ "log",
-+ "thiserror 1.0.69",
-+ "walkdir",
-+ "windows-sys 0.45.0",
-+]
-+
-+[[package]]
-+name = "jni-sys"
-+version = "0.3.0"
-+source = "registry+https://github.com/rust-lang/crates.io-index"
-+checksum = "8eaf4bc02d17cbdd7ff4c7438cafcdf7fb9a4613313ad11b4f8fefe7d3fa0130"
-+
- [[package]]
- name = "jobserver"
- version = "0.1.32"
-@@ -3959,14 +4083,18 @@ dependencies = [
- [[package]]
- name = "mail-send"
- version = "0.5.0"
--source = "registry+https://github.com/rust-lang/crates.io-index"
--checksum = "b12277cdcacfc15af67fe9cf155f31ff68ad8c301304573ea116ed8870f192d5"
- dependencies = [
- "base64 0.22.1",
-+ "env_logger",
- "gethostname",
-+ "mail-auth",
-+ "mail-builder",
-+ "mail-parser",
- "md5",
-+ "rand 0.8.5",
- "rustls 0.23.21",
- "rustls-pki-types",
-+ "rustls-platform-verifier",
- "smtp-proto",
- "tokio",
- "tokio-rustls 0.26.1",
-@@ -5552,6 +5680,7 @@ dependencies = [
- "pin-project-lite",
- "quinn",
- "rustls 0.23.21",
-+ "rustls-native-certs 0.8.1",
- "rustls-pemfile 2.2.0",
- "rustls-pki-types",
- "serde",
-@@ -5920,6 +6049,8 @@ version = "0.23.21"
- source = "registry+https://github.com/rust-lang/crates.io-index"
- checksum = "8f287924602bf649d949c63dc8ac8b235fa5387d394020705b80c4eb597ce5b8"
- dependencies = [
-+ "aws-lc-rs",
-+ "log",
- "once_cell",
- "ring 0.17.8",
- "rustls-pki-types",
-@@ -5937,7 +6068,7 @@ dependencies = [
- "openssl-probe",
- "rustls-pemfile 1.0.4",
- "schannel",
-- "security-framework",
-+ "security-framework 2.11.1",
- ]
-
- [[package]]
-@@ -5950,7 +6081,19 @@ dependencies = [
- "rustls-pemfile 2.2.0",
- "rustls-pki-types",
- "schannel",
-- "security-framework",
-+ "security-framework 2.11.1",
-+]
-+
-+[[package]]
-+name = "rustls-native-certs"
-+version = "0.8.1"
-+source = "registry+https://github.com/rust-lang/crates.io-index"
-+checksum = "7fcff2dd52b58a8d98a70243663a0d234c4e2b79235637849d15913394a247d3"
-+dependencies = [
-+ "openssl-probe",
-+ "rustls-pki-types",
-+ "schannel",
-+ "security-framework 3.2.0",
- ]
-
- [[package]]
-@@ -5980,6 +6123,33 @@ dependencies = [
- "web-time",
- ]
-
-+[[package]]
-+name = "rustls-platform-verifier"
-+version = "0.5.0"
-+source = "registry+https://github.com/rust-lang/crates.io-index"
-+checksum = "e012c45844a1790332c9386ed4ca3a06def221092eda277e6f079728f8ea99da"
-+dependencies = [
-+ "core-foundation 0.10.0",
-+ "core-foundation-sys",
-+ "jni",
-+ "log",
-+ "once_cell",
-+ "rustls 0.23.21",
-+ "rustls-native-certs 0.8.1",
-+ "rustls-platform-verifier-android",
-+ "rustls-webpki 0.102.8",
-+ "security-framework 3.2.0",
-+ "security-framework-sys",
-+ "webpki-root-certs",
-+ "windows-sys 0.52.0",
-+]
-+
-+[[package]]
-+name = "rustls-platform-verifier-android"
-+version = "0.1.1"
-+source = "registry+https://github.com/rust-lang/crates.io-index"
-+checksum = "f87165f0995f63a9fbeea62b64d10b4d9d8e78ec6d7d51fb2125fda7bb36788f"
-+
- [[package]]
- name = "rustls-webpki"
- version = "0.101.7"
-@@ -5996,6 +6166,7 @@ version = "0.102.8"
- source = "registry+https://github.com/rust-lang/crates.io-index"
- checksum = "64ca1bc8749bd4cf37b5ce386cc146580777b4e8572c7b97baf22c83f444bee9"
- dependencies = [
-+ "aws-lc-rs",
- "ring 0.17.8",
- "rustls-pki-types",
- "untrusted 0.9.0",
-@@ -6125,7 +6296,20 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
- checksum = "897b2245f0b511c87893af39b033e5ca9cce68824c4d7e7630b5a1d339658d02"
- dependencies = [
- "bitflags 2.8.0",
-- "core-foundation",
-+ "core-foundation 0.9.4",
-+ "core-foundation-sys",
-+ "libc",
-+ "security-framework-sys",
-+]
-+
-+[[package]]
-+name = "security-framework"
-+version = "3.2.0"
-+source = "registry+https://github.com/rust-lang/crates.io-index"
-+checksum = "271720403f46ca04f7ba6f55d438f8bd878d6b8ca0a1046e8228c4145bcbb316"
-+dependencies = [
-+ "bitflags 2.8.0",
-+ "core-foundation 0.10.0",
- "core-foundation-sys",
- "libc",
- "security-framework-sys",
-@@ -6817,7 +7001,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
- checksum = "ba3a3adc5c275d719af8cb4272ea1c4a6d668a777f37e115f6d11ddbc1c8e0e7"
- dependencies = [
- "bitflags 1.3.2",
-- "core-foundation",
-+ "core-foundation 0.9.4",
- "system-configuration-sys",
- ]
-
-@@ -7569,6 +7753,7 @@ dependencies = [
- "rustls 0.23.21",
- "rustls-pemfile 2.2.0",
- "rustls-pki-types",
-+ "rustls-platform-verifier",
- "serde",
- "serde_json",
- "smtp-proto",
-@@ -7764,6 +7949,15 @@ dependencies = [
- "untrusted 0.9.0",
- ]
-
-+[[package]]
-+name = "webpki-root-certs"
-+version = "0.26.8"
-+source = "registry+https://github.com/rust-lang/crates.io-index"
-+checksum = "09aed61f5e8d2c18344b3faa33a4c837855fe56642757754775548fee21386c4"
-+dependencies = [
-+ "rustls-pki-types",
-+]
-+
- [[package]]
- name = "webpki-roots"
- version = "0.25.4"
-@@ -7789,6 +7983,18 @@ dependencies = [
- "once_cell",
- ]
-
-+[[package]]
-+name = "which"
-+version = "4.4.2"
-+source = "registry+https://github.com/rust-lang/crates.io-index"
-+checksum = "87ba24419a2078cd2b0f2ede2691b6c66d8e47836da3b6db8265ebad47afbfc7"
-+dependencies = [
-+ "either",
-+ "home",
-+ "once_cell",
-+ "rustix",
-+]
-+
- [[package]]
- name = "whoami"
- version = "1.5.2"
-@@ -7886,6 +8092,15 @@ dependencies = [
- "windows-targets 0.52.6",
- ]
-
-+[[package]]
-+name = "windows-sys"
-+version = "0.45.0"
-+source = "registry+https://github.com/rust-lang/crates.io-index"
-+checksum = "75283be5efb2831d37ea142365f009c02ec203cd29a3ebecbc093d52315b66d0"
-+dependencies = [
-+ "windows-targets 0.42.2",
-+]
-+
- [[package]]
- name = "windows-sys"
- version = "0.48.0"
-@@ -7913,6 +8128,21 @@ dependencies = [
- "windows-targets 0.52.6",
- ]
-
-+[[package]]
-+name = "windows-targets"
-+version = "0.42.2"
-+source = "registry+https://github.com/rust-lang/crates.io-index"
-+checksum = "8e5180c00cd44c9b1c88adb3693291f1cd93605ded80c250a75d472756b4d071"
-+dependencies = [
-+ "windows_aarch64_gnullvm 0.42.2",
-+ "windows_aarch64_msvc 0.42.2",
-+ "windows_i686_gnu 0.42.2",
-+ "windows_i686_msvc 0.42.2",
-+ "windows_x86_64_gnu 0.42.2",
-+ "windows_x86_64_gnullvm 0.42.2",
-+ "windows_x86_64_msvc 0.42.2",
-+]
-+
- [[package]]
- name = "windows-targets"
- version = "0.48.5"
-@@ -7944,6 +8174,12 @@ dependencies = [
- "windows_x86_64_msvc 0.52.6",
- ]
-
-+[[package]]
-+name = "windows_aarch64_gnullvm"
-+version = "0.42.2"
-+source = "registry+https://github.com/rust-lang/crates.io-index"
-+checksum = "597a5118570b68bc08d8d59125332c54f1ba9d9adeedeef5b99b02ba2b0698f8"
-+
- [[package]]
- name = "windows_aarch64_gnullvm"
- version = "0.48.5"
-@@ -7956,6 +8192,12 @@ version = "0.52.6"
- source = "registry+https://github.com/rust-lang/crates.io-index"
- checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3"
-
-+[[package]]
-+name = "windows_aarch64_msvc"
-+version = "0.42.2"
-+source = "registry+https://github.com/rust-lang/crates.io-index"
-+checksum = "e08e8864a60f06ef0d0ff4ba04124db8b0fb3be5776a5cd47641e942e58c4d43"
-+
- [[package]]
- name = "windows_aarch64_msvc"
- version = "0.48.5"
-@@ -7968,6 +8210,12 @@ version = "0.52.6"
- source = "registry+https://github.com/rust-lang/crates.io-index"
- checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469"
-
-+[[package]]
-+name = "windows_i686_gnu"
-+version = "0.42.2"
-+source = "registry+https://github.com/rust-lang/crates.io-index"
-+checksum = "c61d927d8da41da96a81f029489353e68739737d3beca43145c8afec9a31a84f"
-+
- [[package]]
- name = "windows_i686_gnu"
- version = "0.48.5"
-@@ -7986,6 +8234,12 @@ version = "0.52.6"
- source = "registry+https://github.com/rust-lang/crates.io-index"
- checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66"
-
-+[[package]]
-+name = "windows_i686_msvc"
-+version = "0.42.2"
-+source = "registry+https://github.com/rust-lang/crates.io-index"
-+checksum = "44d840b6ec649f480a41c8d80f9c65108b92d89345dd94027bfe06ac444d1060"
-+
- [[package]]
- name = "windows_i686_msvc"
- version = "0.48.5"
-@@ -7998,6 +8252,12 @@ version = "0.52.6"
- source = "registry+https://github.com/rust-lang/crates.io-index"
- checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66"
-
-+[[package]]
-+name = "windows_x86_64_gnu"
-+version = "0.42.2"
-+source = "registry+https://github.com/rust-lang/crates.io-index"
-+checksum = "8de912b8b8feb55c064867cf047dda097f92d51efad5b491dfb98f6bbb70cb36"
-+
- [[package]]
- name = "windows_x86_64_gnu"
- version = "0.48.5"
-@@ -8010,6 +8270,12 @@ version = "0.52.6"
- source = "registry+https://github.com/rust-lang/crates.io-index"
- checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78"
-
-+[[package]]
-+name = "windows_x86_64_gnullvm"
-+version = "0.42.2"
-+source = "registry+https://github.com/rust-lang/crates.io-index"
-+checksum = "26d41b46a36d453748aedef1486d5c7a85db22e56aff34643984ea85514e94a3"
-+
- [[package]]
- name = "windows_x86_64_gnullvm"
- version = "0.48.5"
-@@ -8022,6 +8288,12 @@ version = "0.52.6"
- source = "registry+https://github.com/rust-lang/crates.io-index"
- checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d"
-
-+[[package]]
-+name = "windows_x86_64_msvc"
-+version = "0.42.2"
-+source = "registry+https://github.com/rust-lang/crates.io-index"
-+checksum = "9aec5da331524158c6d1a4ac0ab1541149c0b9505fde06423b02f5ef0106b9f0"
-+
- [[package]]
- name = "windows_x86_64_msvc"
- version = "0.48.5"
-diff --git a/crates/cli/Cargo.toml b/crates/cli/Cargo.toml
-index a2d19a06..76866b80 100644
---- a/crates/cli/Cargo.toml
-+++ b/crates/cli/Cargo.toml
-@@ -13,7 +13,7 @@ resolver = "2"
- [dependencies]
- jmap-client = { version = "0.3", features = ["async"] }
- mail-parser = { version = "0.10", features = ["full_encoding", "serde_support"] }
--reqwest = { version = "0.12", default-features = false, features = ["rustls-tls-webpki-roots", "http2"]}
-+reqwest = { version = "0.12", default-features = false, features = ["rustls-tls-native-roots", "http2"]}
- tokio = { version = "1.23", features = ["full"] }
- num_cpus = "1.13.1"
- clap = { version = "4.1.6", features = ["derive"] }
-diff --git a/crates/common/Cargo.toml b/crates/common/Cargo.toml
-index 3da0183f..93c49bb5 100644
---- a/crates/common/Cargo.toml
-+++ b/crates/common/Cargo.toml
-@@ -16,7 +16,7 @@ sieve-rs = { version = "0.6" }
- mail-parser = { version = "0.10", features = ["full_encoding"] }
- mail-builder = { version = "0.4" }
- mail-auth = { version = "0.6" }
--mail-send = { version = "0.5", default-features = false, features = ["cram-md5", "ring", "tls12"] }
-+mail-send = { path = "../mail-send", default-features = false, features = ["cram-md5", "ring", "tls12"] }
- smtp-proto = { version = "0.1", features = ["serde_support"] }
- dns-update = { version = "0.1" }
- ahash = { version = "0.8.2", features = ["serde"] }
-@@ -32,7 +32,7 @@ tokio = { version = "1.23", features = ["net", "macros"] }
- tokio-rustls = { version = "0.26", default-features = false, features = ["ring", "tls12"] }
- futures = "0.3"
- rcgen = "0.12"
--reqwest = { version = "0.12", default-features = false, features = ["rustls-tls-webpki-roots", "http2", "stream"]}
-+reqwest = { version = "0.12", default-features = false, features = ["rustls-tls-native-roots", "http2", "stream"]}
- serde = { version = "1.0", features = ["derive"]}
- serde_json = "1.0"
- base64 = "0.22"
-diff --git a/crates/directory/Cargo.toml b/crates/directory/Cargo.toml
-index dc022e7a..10e0c00a 100644
---- a/crates/directory/Cargo.toml
-+++ b/crates/directory/Cargo.toml
-@@ -12,7 +12,7 @@ trc = { path = "../trc" }
- jmap_proto = { path = "../jmap-proto" }
- smtp-proto = { version = "0.1" }
- mail-parser = { version = "0.10", features = ["full_encoding", "serde_support"] }
--mail-send = { version = "0.5", default-features = false, features = ["cram-md5", "ring", "tls12"] }
-+mail-send = { path = "../mail-send", default-features = false, features = ["cram-md5", "ring", "tls12"] }
- mail-builder = { version = "0.4" }
- tokio = { version = "1.23", features = ["net"] }
- tokio-rustls = { version = "0.26", default-features = false, features = ["ring", "tls12"] }
-@@ -34,7 +34,7 @@ futures = "0.3"
- regex = "1.7.0"
- serde = { version = "1.0", features = ["derive"]}
- totp-rs = { version = "5.5.1", features = ["otpauth"] }
--reqwest = { version = "0.12", default-features = false, features = ["rustls-tls-webpki-roots", "http2"] }
-+reqwest = { version = "0.12", default-features = false, features = ["rustls-tls-native-roots", "http2"] }
- serde_json = "1.0"
- base64 = "0.22"
-
-diff --git a/crates/imap/Cargo.toml b/crates/imap/Cargo.toml
-index 640ca4fd..d91931c1 100644
---- a/crates/imap/Cargo.toml
-+++ b/crates/imap/Cargo.toml
-@@ -16,7 +16,7 @@ email = { path = "../email" }
- nlp = { path = "../nlp" }
- utils = { path = "../utils" }
- mail-parser = { version = "0.10", features = ["full_encoding"] }
--mail-send = { version = "0.5", default-features = false, features = ["cram-md5", "ring", "tls12"] }
-+mail-send = { path = "../mail-send", default-features = false, features = ["cram-md5", "ring", "tls12"] }
- rustls = { version = "0.23.5", default-features = false, features = ["std", "ring", "tls12"] }
- rustls-pemfile = "2.0"
- tokio = { version = "1.23", features = ["full"] }
-diff --git a/crates/jmap/Cargo.toml b/crates/jmap/Cargo.toml
-index 7be56e44..ad5ed795 100644
---- a/crates/jmap/Cargo.toml
-+++ b/crates/jmap/Cargo.toml
-@@ -18,7 +18,7 @@ email = { path = "../email" }
- smtp-proto = { version = "0.1" }
- mail-parser = { version = "0.10", features = ["full_encoding", "serde_support"] }
- mail-builder = { version = "0.4" }
--mail-send = { version = "0.5", default-features = false, features = ["cram-md5", "ring", "tls12"] }
-+mail-send = { path = "../mail-send", default-features = false, features = ["cram-md5", "ring", "tls12"] }
- mail-auth = { version = "0.6", features = ["generate"] }
- sieve-rs = { version = "0.6" }
- serde = { version = "1.0", features = ["derive"]}
-@@ -38,7 +38,7 @@ p256 = { version = "0.13", features = ["ecdh"] }
- hkdf = "0.12.3"
- sha1 = "0.10"
- sha2 = "0.10"
--reqwest = { version = "0.12", default-features = false, features = ["rustls-tls-webpki-roots", "http2"]}
-+reqwest = { version = "0.12", default-features = false, features = ["rustls-tls-native-roots", "http2"]}
- tokio-tungstenite = "0.26"
- tungstenite = "0.26"
- chrono = "0.4"
-diff --git a/crates/mail-send/Cargo.toml b/crates/mail-send/Cargo.toml
-index fb5f402d..6760afab 100644
---- a/crates/mail-send/Cargo.toml
-+++ b/crates/mail-send/Cargo.toml
-@@ -27,6 +27,7 @@ rustls = { version = "0.23", default-features = false, features = ["std"]}
- tokio-rustls = { version = "0.26", default-features = false }
- webpki-roots = { version = "0.26"}
- rustls-pki-types = { version = "1" }
-+rustls-platform-verifier = "0.5"
- gethostname = { version = "0.5"}
-
- [dev-dependencies]
-diff --git a/crates/mail-send/src/smtp/tls.rs b/crates/mail-send/src/smtp/tls.rs
-index b15a6db8..7ddd0798 100644
---- a/crates/mail-send/src/smtp/tls.rs
-+++ b/crates/mail-send/src/smtp/tls.rs
-@@ -12,9 +12,9 @@ use std::{convert::TryFrom, io, sync::Arc};
-
- use rustls::{
- client::danger::{HandshakeSignatureValid, ServerCertVerified, ServerCertVerifier},
-- ClientConfig, ClientConnection, RootCertStore, SignatureScheme,
-+ ClientConfig, ClientConnection, SignatureScheme,
- };
--use rustls_pki_types::{ServerName, TrustAnchor};
-+use rustls_pki_types::ServerName;
- use tokio::net::TcpStream;
- use tokio_rustls::{client::TlsStream, TlsConnector};
-
-@@ -78,20 +78,14 @@ impl SmtpClient<TlsStream<TcpStream>> {
- }
-
- pub fn build_tls_connector(allow_invalid_certs: bool) -> TlsConnector {
-+ use rustls_platform_verifier::BuilderVerifierExt;
-+
-+ let config = ClientConfig::builder();
-+
- let config = if !allow_invalid_certs {
-- let mut root_cert_store = RootCertStore::empty();
--
-- root_cert_store.extend(webpki_roots::TLS_SERVER_ROOTS.iter().map(|ta| TrustAnchor {
-- subject: ta.subject.clone(),
-- subject_public_key_info: ta.subject_public_key_info.clone(),
-- name_constraints: ta.name_constraints.clone(),
-- }));
--
-- ClientConfig::builder()
-- .with_root_certificates(root_cert_store)
-- .with_no_client_auth()
-+ config.with_platform_verifier().with_no_client_auth()
- } else {
-- ClientConfig::builder()
-+ config
- .dangerous()
- .with_custom_certificate_verifier(Arc::new(DummyVerifier {}))
- .with_no_client_auth()
-diff --git a/crates/managesieve/Cargo.toml b/crates/managesieve/Cargo.toml
-index 650ab23b..42738e68 100644
---- a/crates/managesieve/Cargo.toml
-+++ b/crates/managesieve/Cargo.toml
-@@ -15,7 +15,7 @@ store = { path = "../store" }
- utils = { path = "../utils" }
- trc = { path = "../trc" }
- mail-parser = { version = "0.10", features = ["full_encoding"] }
--mail-send = { version = "0.5", default-features = false, features = ["cram-md5", "ring", "tls12"] }
-+mail-send = { path = "../mail-send", default-features = false, features = ["cram-md5", "ring", "tls12"] }
- sieve-rs = { version = "0.6" }
- rustls = { version = "0.23.5", default-features = false, features = ["std", "ring", "tls12"] }
- rustls-pemfile = "2.0"
-diff --git a/crates/pop3/Cargo.toml b/crates/pop3/Cargo.toml
-index 5f86ed00..89e7b732 100644
---- a/crates/pop3/Cargo.toml
-+++ b/crates/pop3/Cargo.toml
-@@ -15,7 +15,7 @@ trc = { path = "../trc" }
- jmap_proto = { path = "../jmap-proto" }
- email = { path = "../email" }
- mail-parser = { version = "0.10", features = ["full_encoding"] }
--mail-send = { version = "0.5", default-features = false, features = ["cram-md5", "ring", "tls12"] }
-+mail-send = { path = "../mail-send", default-features = false, features = ["cram-md5", "ring", "tls12"] }
- rustls = { version = "0.23.5", default-features = false, features = ["std", "ring", "tls12"] }
- tokio = { version = "1.23", features = ["full"] }
- tokio-rustls = { version = "0.26", default-features = false, features = ["ring", "tls12"] }
-diff --git a/crates/smtp/Cargo.toml b/crates/smtp/Cargo.toml
-index 5997c1c3..5f5badc2 100644
---- a/crates/smtp/Cargo.toml
-+++ b/crates/smtp/Cargo.toml
-@@ -21,7 +21,7 @@ email = { path = "../email" }
- spam-filter = { path = "../spam-filter" }
- trc = { path = "../trc" }
- mail-auth = { version = "0.6" }
--mail-send = { version = "0.5", default-features = false, features = ["cram-md5", "ring", "tls12"] }
-+mail-send = { path = "../mail-send", default-features = false, features = ["cram-md5", "ring", "tls12"] }
- mail-parser = { version = "0.10", features = ["full_encoding"] }
- mail-builder = { version = "0.4" }
- smtp-proto = { version = "0.1", features = ["serde_support"] }
-@@ -47,7 +47,7 @@ blake3 = "1.3"
- lru-cache = "0.1.2"
- rand = "0.8.5"
- x509-parser = "0.16.0"
--reqwest = { version = "0.12", default-features = false, features = ["rustls-tls-webpki-roots", "http2"] }
-+reqwest = { version = "0.12", default-features = false, features = ["rustls-tls-native-roots", "http2"] }
- serde = { version = "1.0", features = ["derive", "rc"] }
- serde_json = "1.0"
- num_cpus = "1.15.0"
-diff --git a/crates/spam-filter/Cargo.toml b/crates/spam-filter/Cargo.toml
-index f5b63353..c9176cf6 100644
---- a/crates/spam-filter/Cargo.toml
-+++ b/crates/spam-filter/Cargo.toml
-@@ -14,12 +14,12 @@ smtp-proto = { version = "0.1", features = ["serde_support"] }
- mail-parser = { version = "0.10", features = ["full_encoding"] }
- mail-builder = { version = "0.4" }
- mail-auth = { version = "0.6" }
--mail-send = { version = "0.5", default-features = false, features = ["cram-md5", "ring", "tls12"] }
-+mail-send = { path = "../mail-send", default-features = false, features = ["cram-md5", "ring", "tls12"] }
- tokio = { version = "1.23", features = ["net", "macros"] }
- psl = "2"
- hyper = { version = "1.0.1", features = ["server", "http1", "http2"] }
- idna = "1.0"
--reqwest = { version = "0.12", default-features = false, features = ["rustls-tls-webpki-roots", "http2", "stream"]}
-+reqwest = { version = "0.12", default-features = false, features = ["rustls-tls-native-roots", "http2", "stream"]}
- decancer = "3.0.1"
- unicode-security = "0.1.0"
- infer = "0.16"
-diff --git a/crates/store/Cargo.toml b/crates/store/Cargo.toml
-index b0cf7d77..67c2d742 100644
---- a/crates/store/Cargo.toml
-+++ b/crates/store/Cargo.toml
-@@ -15,7 +15,7 @@ rust-s3 = { version = "=0.35.0-alpha.2", default-features = false, features = ["
- azure_core = { version = "0.21.0", optional = true }
- azure_storage = { version = "0.21.0", default-features = false, features = ["enable_reqwest_rustls", "hmac_rust"], optional = true }
- azure_storage_blobs = { version = "0.21.0", default-features = false, features = ["enable_reqwest_rustls", "hmac_rust"], optional = true }
--reqwest = { version = "0.12", default-features = false, features = ["rustls-tls-webpki-roots", "http2", "stream"]}
-+reqwest = { version = "0.12", default-features = false, features = ["rustls-tls-native-roots", "http2", "stream"]}
- tokio = { version = "1.23", features = ["sync", "fs", "io-util"] }
- r2d2 = { version = "0.8.10", optional = true }
- futures = { version = "0.3", optional = true }
-diff --git a/crates/trc/Cargo.toml b/crates/trc/Cargo.toml
-index e4f2ca7c..f294e469 100644
---- a/crates/trc/Cargo.toml
-+++ b/crates/trc/Cargo.toml
-@@ -11,7 +11,7 @@ mail-parser = { version = "0.10", features = ["full_encoding"] }
- base64 = "0.22.1"
- serde = "1.0"
- serde_json = "1.0.120"
--reqwest = { version = "0.12", default-features = false, features = ["rustls-tls-webpki-roots", "http2"]}
-+reqwest = { version = "0.12", default-features = false, features = ["rustls-tls-native-roots", "http2"]}
- bincode = "1.3.3"
- rtrb = "0.3.1"
- parking_lot = "0.12.3"
-diff --git a/crates/utils/Cargo.toml b/crates/utils/Cargo.toml
-index e0a7ef9d..14b1d675 100644
---- a/crates/utils/Cargo.toml
-+++ b/crates/utils/Cargo.toml
-@@ -9,12 +9,13 @@ trc = { path = "../trc" }
- rustls = { version = "0.23.5", default-features = false, features = ["std", "ring", "tls12"] }
- rustls-pemfile = "2.0"
- rustls-pki-types = { version = "1" }
-+rustls-platform-verifier = "0.5"
- tokio = { version = "1.23", features = ["net", "macros"] }
- tokio-rustls = { version = "0.26", default-features = false, features = ["ring", "tls12"] }
- serde = { version = "1.0", features = ["derive"]}
- mail-auth = { version = "0.6" }
- smtp-proto = { version = "0.1" }
--mail-send = { version = "0.5", default-features = false, features = ["cram-md5", "ring", "tls12"] }
-+mail-send = { path = "../mail-send", default-features = false, features = ["cram-md5", "ring", "tls12"] }
- ahash = { version = "0.8" }
- chrono = "0.4"
- rand = "0.8.5"
-@@ -23,7 +24,7 @@ ring = { version = "0.17" }
- base64 = "0.22"
- serde_json = "1.0"
- rcgen = "0.13"
--reqwest = { version = "0.12", default-features = false, features = ["rustls-tls-webpki-roots", "http2", "stream"]}
-+reqwest = { version = "0.12", default-features = false, features = ["rustls-tls-native-roots", "http2", "stream"]}
- x509-parser = "0.16.0"
- pem = "3.0"
- parking_lot = "0.12"
-diff --git a/crates/utils/src/lib.rs b/crates/utils/src/lib.rs
-index acec2f04..b2cdaf65 100644
---- a/crates/utils/src/lib.rs
-+++ b/crates/utils/src/lib.rs
-@@ -18,9 +18,9 @@ use futures::StreamExt;
- use reqwest::Response;
- use rustls::{
- client::danger::{HandshakeSignatureValid, ServerCertVerified, ServerCertVerifier},
-- ClientConfig, RootCertStore, SignatureScheme,
-+ ClientConfig, SignatureScheme,
- };
--use rustls_pki_types::TrustAnchor;
-+use rustls_platform_verifier::BuilderVerifierExt;
-
- pub const BLOB_HASH_LEN: usize = 32;
-
-@@ -280,17 +280,7 @@ pub fn rustls_client_config(allow_invalid_certs: bool) -> ClientConfig {
- let config = ClientConfig::builder();
-
- if !allow_invalid_certs {
-- let mut root_cert_store = RootCertStore::empty();
--
-- root_cert_store.extend(webpki_roots::TLS_SERVER_ROOTS.iter().map(|ta| TrustAnchor {
-- subject: ta.subject.clone(),
-- subject_public_key_info: ta.subject_public_key_info.clone(),
-- name_constraints: ta.name_constraints.clone(),
-- }));
--
-- config
-- .with_root_certificates(root_cert_store)
-- .with_no_client_auth()
-+ config.with_platform_verifier().with_no_client_auth()
- } else {
- config
- .dangerous()
-diff --git a/tests/Cargo.toml b/tests/Cargo.toml
-index 6aa6d35b..256a574b 100644
---- a/tests/Cargo.toml
-+++ b/tests/Cargo.toml
-@@ -34,12 +34,12 @@ spam-filter = { path = "../crates/spam-filter", features = ["test_mode", "enterp
- trc = { path = "../crates/trc" }
- managesieve = { path = "../crates/managesieve", features = ["test_mode", "enterprise"] }
- smtp-proto = { version = "0.1" }
--mail-send = { version = "0.5", default-features = false, features = ["cram-md5", "ring", "tls12"] }
-+mail-send = { path = "../crates/mail-send", default-features = false, features = ["cram-md5", "ring", "tls12"] }
- mail-auth = { version = "0.6", features = ["test"] }
--sieve-rs = { version = "0.6" }
-+sieve-rs = { version = "0.6" }
- utils = { path = "../crates/utils", features = ["test_mode"] }
--jmap-client = { version = "0.3", features = ["websockets", "debug", "async"] }
--mail-parser = { version = "0.10", features = ["full_encoding", "serde_support"] }
-+jmap-client = { version = "0.3", features = ["websockets", "debug", "async"] }
-+mail-parser = { version = "0.10", features = ["full_encoding", "serde_support"] }
- tokio = { version = "1.23", features = ["full"] }
- tokio-rustls = { version = "0.26", default-features = false, features = ["ring", "tls12"] }
- rustls = { version = "0.23.5", default-features = false, features = ["std", "ring", "tls12"] }
-@@ -50,7 +50,7 @@ rayon = { version = "1.5.1" }
- flate2 = { version = "1.0.17", features = ["zlib"], default-features = false }
- serde = { version = "1.0", features = ["derive"]}
- serde_json = "1.0"
--reqwest = { version = "0.12", default-features = false, features = ["rustls-tls-webpki-roots", "multipart", "http2"]}
-+reqwest = { version = "0.12", default-features = false, features = ["rustls-tls-native-roots", "multipart", "http2"]}
- bytes = "1.4.0"
- futures = "0.3"
- ece = "2.2"
---
-2.47.2
-
diff --git a/pkgs/by-name/st/stalwart-mail-patched/patches/use-platform-ca-roots.patch b/pkgs/by-name/st/stalwart-mail-patched/patches/use-platform-ca-roots.patch
new file mode 100644
index 0000000..392fbde
--- /dev/null
+++ b/pkgs/by-name/st/stalwart-mail-patched/patches/use-platform-ca-roots.patch
@@ -0,0 +1,747 @@
+From 66227b07c6cb4781a38fe603c2e856c5696e0f94 Mon Sep 17 00:00:00 2001
+From: aszlig <aszlig@nix.build>
+Date: Wed, 21 May 2025 19:03:55 +0200
+Subject: [PATCH] Allow to switch to operating system CA root store
+
+So far, we only used the root CA certificates from the "webpki-roots"
+crate, which makes it very difficult if you want to run a custom CA.
+
+In my case I'm running automated tests of full production systems, which
+also includes an ACME setup that injects a Pebble instance into the
+system's CA certificates.
+
+Since Stalwart doesn't use the system's root CA certificate store, it's
+very difficult to inject the certificate for the Pebble instance.
+
+Given that there's also some interest (issue #247) for doing this in an
+enterprise environment with intranet CAs, I decided to generalise this
+far enough that it can not only be used in my downstream setup.
+
+Currently, this doesn't fully address the issue, since in the long term
+this might be something we'd want to configure at runtime, as per
+@mdecimus's comment[1]:
+
+> A slightly more complex approach is required that allows the user to
+> select at runtime which CA store to use.
+
+However, this makes it at least easier to switch to native root CA store
+by simply recompiling with the "tls-native-roots" feature.
+
+[1]: https://github.com/stalwartlabs/stalwart/issues/247#issuecomment-2437039500
+
+Signed-off-by: aszlig <aszlig@nix.build>
+Issue: https://github.com/stalwartlabs/stalwart/issues/247
+---
+ Cargo.lock | 4 ++
+ crates/cli/Cargo.toml | 10 +++--
+ crates/cli/src/main.rs | 8 ++--
+ crates/common/Cargo.toml | 2 +-
+ crates/common/src/enterprise/llm.rs | 2 +-
+ crates/common/src/enterprise/mod.rs | 18 +++++---
+ crates/common/src/lib.rs | 3 +-
+ crates/common/src/listener/acme/directory.rs | 6 +--
+ crates/common/src/manager/mod.rs | 2 +-
+ crates/common/src/scripts/plugins/http.rs | 2 +-
+ crates/common/src/telemetry/webhooks/mod.rs | 2 +-
+ crates/directory/Cargo.toml | 2 +-
+ crates/directory/src/backend/oidc/lookup.rs | 4 +-
+ crates/jmap/Cargo.toml | 2 +-
+ crates/main/Cargo.toml | 1 +
+ crates/services/Cargo.toml | 2 +-
+ crates/services/src/state_manager/http.rs | 2 +-
+ crates/smtp/Cargo.toml | 2 +-
+ crates/smtp/src/inbound/hooks/client.rs | 2 +-
+ crates/smtp/src/outbound/mta_sts/lookup.rs | 3 +-
+ crates/smtp/src/reporting/tls.rs | 5 +--
+ crates/spam-filter/Cargo.toml | 2 +-
+ crates/spam-filter/src/analysis/url.rs | 2 +-
+ crates/store/Cargo.toml | 2 +-
+ crates/store/src/backend/azure/mod.rs | 2 +-
+ crates/store/src/backend/http/lookup.rs | 2 +-
+ crates/trc/Cargo.toml | 2 +-
+ crates/utils/Cargo.toml | 2 +
+ crates/utils/src/lib.rs | 46 ++++++++++++++------
+ crates/utils/src/suffixlist.rs | 7 ++-
+ tests/Cargo.toml | 2 +-
+ tests/src/jmap/auth_oauth.rs | 6 +--
+ tests/src/jmap/mod.rs | 4 +-
+ tests/src/smtp/management/queue.rs | 2 +-
+ tests/src/webdav/mod.rs | 2 +-
+ 35 files changed, 103 insertions(+), 64 deletions(-)
+
+diff --git a/Cargo.lock b/Cargo.lock
+index 0eeb42510..6dd394dc3 100644
+--- a/Cargo.lock
++++ b/Cargo.lock
+@@ -3372,6 +3372,7 @@ dependencies = [
+ "hyper 1.6.0",
+ "hyper-util",
+ "rustls 0.23.27",
++ "rustls-native-certs 0.8.1",
+ "rustls-pki-types",
+ "tokio",
+ "tokio-rustls 0.26.2",
+@@ -6318,6 +6319,7 @@ dependencies = [
+ "pin-project-lite",
+ "quinn",
+ "rustls 0.23.27",
++ "rustls-native-certs 0.8.1",
+ "rustls-pemfile 2.2.0",
+ "rustls-pki-types",
+ "serde",
+@@ -7694,6 +7696,7 @@ dependencies = [
+ "serde",
+ "serde_json",
+ "tokio",
++ "utils",
+ ]
+
+ [[package]]
+@@ -8795,6 +8798,7 @@ dependencies = [
+ "rustls 0.23.27",
+ "rustls-pemfile 2.2.0",
+ "rustls-pki-types",
++ "rustls-platform-verifier",
+ "serde",
+ "serde_json",
+ "smtp-proto",
+diff --git a/crates/cli/Cargo.toml b/crates/cli/Cargo.toml
+index 5573df819..719441a0c 100644
+--- a/crates/cli/Cargo.toml
++++ b/crates/cli/Cargo.toml
+@@ -11,9 +11,9 @@ readme = "README.md"
+ resolver = "2"
+
+ [dependencies]
+-jmap-client = { version = "0.3", features = ["async"] }
+-mail-parser = { version = "0.11", features = ["full_encoding", "serde"] }
+-reqwest = { version = "0.12", default-features = false, features = ["rustls-tls-webpki-roots", "http2"]}
++jmap-client = { version = "0.3", features = ["async"] }
++mail-parser = { version = "0.11", features = ["full_encoding", "serde"] }
++reqwest = { version = "0.12", default-features = false, features = ["http2"]}
+ tokio = { version = "1.45", features = ["full"] }
+ num_cpus = "1.13.1"
+ clap = { version = "4.1.6", features = ["derive"] }
+@@ -30,3 +30,7 @@ futures = "0.3.28"
+ pwhash = "1.0.0"
+ rand = "0.9.0"
+ mail-auth = { version = "0.7" }
++utils.path = "../utils"
++
++[features]
++tls-native-roots = ["utils/tls-native-roots"]
+diff --git a/crates/cli/src/main.rs b/crates/cli/src/main.rs
+index f295c217f..f62d528c5 100644
+--- a/crates/cli/src/main.rs
++++ b/crates/cli/src/main.rs
+@@ -86,7 +86,7 @@ fn parse_credentials(credentials: &str) -> Credentials {
+
+ async fn oauth(url: &str) -> Credentials {
+ let metadata: HashMap<String, serde_json::Value> = serde_json::from_slice(
+- &reqwest::Client::builder()
++ &utils::reqwest_client_builder()
+ .danger_accept_invalid_certs(is_localhost(url))
+ .build()
+ .unwrap_or_default()
+@@ -104,7 +104,7 @@ async fn oauth(url: &str) -> Credentials {
+ let mut params: HashMap<String, String> =
+ HashMap::from_iter([("client_id".to_string(), "Stalwart_CLI".to_string())]);
+ let response: HashMap<String, serde_json::Value> = serde_json::from_slice(
+- &reqwest::Client::builder()
++ &utils::reqwest_client_builder()
+ .danger_accept_invalid_certs(is_localhost(url))
+ .build()
+ .unwrap_or_default()
+@@ -138,7 +138,7 @@ async fn oauth(url: &str) -> Credentials {
+ std::io::stdin().lock().lines().next();
+
+ let mut response: HashMap<String, serde_json::Value> = serde_json::from_slice(
+- &reqwest::Client::builder()
++ &utils::reqwest_client_builder()
+ .danger_accept_invalid_certs(is_localhost(url))
+ .build()
+ .unwrap_or_default()
+@@ -230,7 +230,7 @@ impl Client {
+ },
+ url
+ );
+- let mut request = reqwest::Client::builder()
++ let mut request = utils::reqwest_client_builder()
+ .danger_accept_invalid_certs(is_localhost(&url))
+ .timeout(Duration::from_secs(self.timeout.unwrap_or(60)))
+ .build()
+diff --git a/crates/common/Cargo.toml b/crates/common/Cargo.toml
+index 58028bad3..5add0a5dc 100644
+--- a/crates/common/Cargo.toml
++++ b/crates/common/Cargo.toml
+@@ -33,7 +33,7 @@ tokio = { version = "1.45", features = ["net", "macros"] }
+ tokio-rustls = { version = "0.26", default-features = false, features = ["ring", "tls12"] }
+ futures = "0.3"
+ rcgen = "0.12"
+-reqwest = { version = "0.12", default-features = false, features = ["rustls-tls-webpki-roots", "http2", "stream"]}
++reqwest = { version = "0.12", default-features = false, features = ["http2", "stream"]}
+ serde = { version = "1.0", features = ["derive"]}
+ serde_json = "1.0"
+ base64 = "0.22"
+diff --git a/crates/common/src/enterprise/llm.rs b/crates/common/src/enterprise/llm.rs
+index 8338f39d3..fe013c0f0 100644
+--- a/crates/common/src/enterprise/llm.rs
++++ b/crates/common/src/enterprise/llm.rs
+@@ -125,7 +125,7 @@ impl AiApiConfig {
+ };
+
+ // Send request
+- let response = reqwest::Client::builder()
++ let response = utils::reqwest_client_builder()
+ .timeout(self.timeout)
+ .danger_accept_invalid_certs(self.tls_allow_invalid_certs)
+ .build()
+diff --git a/crates/common/src/enterprise/mod.rs b/crates/common/src/enterprise/mod.rs
+index 9fc8de495..ddaf27880 100644
+--- a/crates/common/src/enterprise/mod.rs
++++ b/crates/common/src/enterprise/mod.rs
+@@ -188,12 +188,18 @@ impl Server {
+
+ let mut logo = None;
+ if let Some(logo_url) = logo_url {
+- let response = reqwest::get(logo_url.as_str()).await.map_err(|err| {
+- trc::ResourceEvent::DownloadExternal
+- .into_err()
+- .details("Failed to download logo")
+- .reason(err)
+- })?;
++ let response = utils::reqwest_client_builder()
++ .build()
++ .unwrap_or_default()
++ .get(logo_url.as_str())
++ .send()
++ .await
++ .map_err(|err| {
++ trc::ResourceEvent::DownloadExternal
++ .into_err()
++ .details("Failed to download logo")
++ .reason(err)
++ })?;
+
+ let content_type = response
+ .headers()
+diff --git a/crates/common/src/lib.rs b/crates/common/src/lib.rs
+index ce4b41b89..185508d1f 100644
+--- a/crates/common/src/lib.rs
++++ b/crates/common/src/lib.rs
+@@ -46,6 +46,8 @@ use utils::{
+ snowflake::SnowflakeIdGenerator,
+ };
+
++pub use utils::USER_AGENT;
++
+ pub mod addresses;
+ pub mod auth;
+ pub mod config;
+@@ -67,7 +69,6 @@ pub use psl;
+ pub static VERSION_PRIVATE: &str = env!("CARGO_PKG_VERSION");
+ pub static VERSION_PUBLIC: &str = "1.0.0";
+
+-pub static USER_AGENT: &str = "Stalwart/1.0.0";
+ pub static DAEMON_NAME: &str = concat!("Stalwart v", env!("CARGO_PKG_VERSION"),);
+ pub static PROD_ID: &str = "-//Stalwart Labs Ltd.//Stalwart Server//EN";
+
+diff --git a/crates/common/src/listener/acme/directory.rs b/crates/common/src/listener/acme/directory.rs
+index f095e1969..6f9cfa0e0 100644
+--- a/crates/common/src/listener/acme/directory.rs
++++ b/crates/common/src/listener/acme/directory.rs
+@@ -7,7 +7,6 @@ use super::jose::{
+ };
+ use base64::Engine;
+ use base64::engine::general_purpose::URL_SAFE_NO_PAD;
+-use hyper::header::USER_AGENT;
+ use rcgen::{Certificate, CustomExtension, PKCS_ECDSA_P256_SHA256};
+ use reqwest::header::CONTENT_TYPE;
+ use reqwest::{Method, Response};
+@@ -316,7 +315,7 @@ async fn https(
+ body: Option<String>,
+ ) -> trc::Result<Response> {
+ let url = url.as_ref();
+- let mut builder = reqwest::Client::builder()
++ let mut builder = utils::reqwest_client_builder()
+ .timeout(Duration::from_secs(30))
+ .http1_only();
+
+@@ -330,8 +329,7 @@ async fn https(
+ let mut request = builder
+ .build()
+ .map_err(|err| trc::EventType::Acme(trc::AcmeEvent::Error).from_http_error(err))?
+- .request(method, url)
+- .header(USER_AGENT, crate::USER_AGENT);
++ .request(method, url);
+
+ if let Some(body) = body {
+ request = request
+diff --git a/crates/common/src/manager/mod.rs b/crates/common/src/manager/mod.rs
+index 51861a82c..a59e74b56 100644
+--- a/crates/common/src/manager/mod.rs
++++ b/crates/common/src/manager/mod.rs
+@@ -76,7 +76,7 @@ pub async fn fetch_resource(
+ .await
+ .map_err(|err| format!("Failed to read {path}: {err}"))
+ } else {
+- let response = reqwest::Client::builder()
++ let response = utils::reqwest_client_builder()
+ .timeout(timeout)
+ .danger_accept_invalid_certs(is_localhost_url(url))
+ .user_agent(USER_AGENT)
+diff --git a/crates/common/src/scripts/plugins/http.rs b/crates/common/src/scripts/plugins/http.rs
+index 42e2af553..54d906e17 100644
+--- a/crates/common/src/scripts/plugins/http.rs
++++ b/crates/common/src/scripts/plugins/http.rs
+@@ -26,7 +26,7 @@ pub async fn exec_header(ctx: PluginContext<'_>) -> trc::Result<Variable> {
+ return Ok(Variable::from(url.split_once("/?").unwrap().1.to_string()));
+ }
+
+- reqwest::Client::builder()
++ utils::reqwest_client_builder()
+ .user_agent(agent.as_ref())
+ .timeout(Duration::from_millis(timeout))
+ .redirect(Policy::none())
+diff --git a/crates/common/src/telemetry/webhooks/mod.rs b/crates/common/src/telemetry/webhooks/mod.rs
+index a70005399..2cebfd81f 100644
+--- a/crates/common/src/telemetry/webhooks/mod.rs
++++ b/crates/common/src/telemetry/webhooks/mod.rs
+@@ -148,7 +148,7 @@ async fn post_webhook_events(
+ }
+
+ // Send request
+- let response = reqwest::Client::builder()
++ let response = utils::reqwest_client_builder()
+ .timeout(settings.timeout)
+ .danger_accept_invalid_certs(settings.tls_allow_invalid_certs)
+ .build()
+diff --git a/crates/directory/Cargo.toml b/crates/directory/Cargo.toml
+index ae8eca025..4a033834b 100644
+--- a/crates/directory/Cargo.toml
++++ b/crates/directory/Cargo.toml
+@@ -35,7 +35,7 @@ futures = "0.3"
+ regex = "1.7.0"
+ serde = { version = "1.0", features = ["derive"]}
+ totp-rs = { version = "5.5.1", features = ["otpauth"] }
+-reqwest = { version = "0.12", default-features = false, features = ["rustls-tls-webpki-roots", "http2"] }
++reqwest = { version = "0.12", default-features = false, features = ["http2"] }
+ serde_json = "1.0"
+ base64 = "0.22"
+ rkyv = { version = "0.8.10", features = ["little_endian"] }
+diff --git a/crates/directory/src/backend/oidc/lookup.rs b/crates/directory/src/backend/oidc/lookup.rs
+index 755064115..907831a46 100644
+--- a/crates/directory/src/backend/oidc/lookup.rs
++++ b/crates/directory/src/backend/oidc/lookup.rs
+@@ -36,10 +36,10 @@ impl OpenIdDirectory {
+ QueryBy::Credentials(Credentials::OAuthBearer { token }) => {
+ // Send request
+ #[cfg(feature = "test_mode")]
+- let client = reqwest::Client::builder().danger_accept_invalid_certs(true);
++ let client = utils::reqwest_client_builder().danger_accept_invalid_certs(true);
+
+ #[cfg(not(feature = "test_mode"))]
+- let client = reqwest::Client::builder();
++ let client = utils::reqwest_client_builder();
+
+ let client = client
+ .timeout(self.config.endpoint_timeout)
+diff --git a/crates/jmap/Cargo.toml b/crates/jmap/Cargo.toml
+index 9d9cfa7d7..a3a7b5003 100644
+--- a/crates/jmap/Cargo.toml
++++ b/crates/jmap/Cargo.toml
+@@ -36,7 +36,7 @@ p256 = { version = "0.13", features = ["ecdh"] }
+ hkdf = "0.12.3"
+ sha1 = "0.10"
+ sha2 = "0.10"
+-reqwest = { version = "0.12", default-features = false, features = ["rustls-tls-webpki-roots", "http2"]}
++reqwest = { version = "0.12", default-features = false, features = ["http2"]}
+ tokio-tungstenite = "0.26"
+ tungstenite = "0.26"
+ chrono = "0.4"
+diff --git a/crates/main/Cargo.toml b/crates/main/Cargo.toml
+index 1023b73bf..4b39dae29 100644
+--- a/crates/main/Cargo.toml
++++ b/crates/main/Cargo.toml
+@@ -64,3 +64,4 @@ enterprise = [ "jmap/enterprise",
+ "dav/enterprise",
+ "groupware/enterprise",
+ "services/enterprise" ]
++tls-native-roots = ["utils/tls-native-roots"]
+diff --git a/crates/services/Cargo.toml b/crates/services/Cargo.toml
+index 11bb76e5a..35aa0b6cb 100644
+--- a/crates/services/Cargo.toml
++++ b/crates/services/Cargo.toml
+@@ -24,7 +24,7 @@ rsa = "0.9.2"
+ p256 = { version = "0.13", features = ["ecdh"] }
+ hkdf = "0.12.3"
+ sha2 = "0.10"
+-reqwest = { version = "0.12", default-features = false, features = ["rustls-tls-webpki-roots", "http2"]}
++reqwest = { version = "0.12", default-features = false, features = ["http2"]}
+ base64 = "0.22"
+ compact_str = "0.9.0"
+
+diff --git a/crates/services/src/state_manager/http.rs b/crates/services/src/state_manager/http.rs
+index edd01865f..ee26b8482 100644
+--- a/crates/services/src/state_manager/http.rs
++++ b/crates/services/src/state_manager/http.rs
+@@ -63,7 +63,7 @@ pub(crate) async fn http_request(
+ keys: Option<EncryptionKeys>,
+ push_timeout: Duration,
+ ) -> bool {
+- let client_builder = reqwest::Client::builder().timeout(push_timeout);
++ let client_builder = utils::reqwest_client_builder().timeout(push_timeout);
+
+ #[cfg(feature = "test_mode")]
+ let client_builder = client_builder.danger_accept_invalid_certs(true);
+diff --git a/crates/smtp/Cargo.toml b/crates/smtp/Cargo.toml
+index e4a781796..d21665c92 100644
+--- a/crates/smtp/Cargo.toml
++++ b/crates/smtp/Cargo.toml
+@@ -47,7 +47,7 @@ blake3 = "1.3"
+ lru-cache = "0.1.2"
+ rand = "0.9.0"
+ x509-parser = "0.17.0"
+-reqwest = { version = "0.12", default-features = false, features = ["rustls-tls-webpki-roots", "http2"] }
++reqwest = { version = "0.12", default-features = false, features = ["http2"] }
+ serde = { version = "1.0", features = ["derive", "rc"] }
+ serde_json = "1.0"
+ num_cpus = "1.15.0"
+diff --git a/crates/smtp/src/inbound/hooks/client.rs b/crates/smtp/src/inbound/hooks/client.rs
+index 2e3a9e17c..6bfb40a63 100644
+--- a/crates/smtp/src/inbound/hooks/client.rs
++++ b/crates/smtp/src/inbound/hooks/client.rs
+@@ -13,7 +13,7 @@ pub(super) async fn send_mta_hook_request(
+ mta_hook: &MTAHook,
+ request: Request,
+ ) -> Result<Response, String> {
+- let response = reqwest::Client::builder()
++ let response = utils::reqwest_client_builder()
+ .timeout(mta_hook.timeout)
+ .danger_accept_invalid_certs(mta_hook.tls_allow_invalid_certs)
+ .build()
+diff --git a/crates/smtp/src/outbound/mta_sts/lookup.rs b/crates/smtp/src/outbound/mta_sts/lookup.rs
+index c8b279c2f..10f2aafeb 100644
+--- a/crates/smtp/src/outbound/mta_sts/lookup.rs
++++ b/crates/smtp/src/outbound/mta_sts/lookup.rs
+@@ -67,8 +67,7 @@ impl MtaStsLookup for Server {
+
+ // Fetch policy
+ #[cfg(not(feature = "test_mode"))]
+- let bytes = reqwest::Client::builder()
+- .user_agent(common::USER_AGENT)
++ let bytes = utils::reqwest_client_builder()
+ .timeout(timeout)
+ .redirect(reqwest::redirect::Policy::none())
+ .build()?
+diff --git a/crates/smtp/src/reporting/tls.rs b/crates/smtp/src/reporting/tls.rs
+index 28e9fa47b..875f395c8 100644
+--- a/crates/smtp/src/reporting/tls.rs
++++ b/crates/smtp/src/reporting/tls.rs
+@@ -8,7 +8,7 @@ use super::{AggregateTimestamp, SerializedSize};
+ use crate::{queue::RecipientDomain, reporting::SmtpReporting};
+ use ahash::AHashMap;
+ use common::{
+- Server, USER_AGENT,
++ Server,
+ config::smtp::{
+ report::AggregateFrequency,
+ resolver::{Mode, MxPattern},
+@@ -142,8 +142,7 @@ impl TlsReporting for Server {
+ for uri in &rua {
+ match uri {
+ ReportUri::Http(uri) => {
+- if let Ok(client) = reqwest::Client::builder()
+- .user_agent(USER_AGENT)
++ if let Ok(client) = utils::reqwest_client_builder()
+ .timeout(Duration::from_secs(2 * 60))
+ .build()
+ {
+diff --git a/crates/spam-filter/Cargo.toml b/crates/spam-filter/Cargo.toml
+index f6ec739a8..5a3df8453 100644
+--- a/crates/spam-filter/Cargo.toml
++++ b/crates/spam-filter/Cargo.toml
+@@ -19,7 +19,7 @@ tokio = { version = "1.45", features = ["net", "macros"] }
+ psl = "2"
+ hyper = { version = "1.0.1", features = ["server", "http1", "http2"] }
+ idna = "1.0"
+-reqwest = { version = "0.12", default-features = false, features = ["rustls-tls-webpki-roots", "http2", "stream"]}
++reqwest = { version = "0.12", default-features = false, features = ["http2", "stream"]}
+ decancer = "3.0.1"
+ unicode-security = "0.1.0"
+ infer = "0.19"
+diff --git a/crates/spam-filter/src/analysis/url.rs b/crates/spam-filter/src/analysis/url.rs
+index a0d663917..e6377f13c 100644
+--- a/crates/spam-filter/src/analysis/url.rs
++++ b/crates/spam-filter/src/analysis/url.rs
+@@ -290,7 +290,7 @@ async fn http_get_header(
+ Ok(None)
+ };
+ }
+- reqwest::Client::builder()
++ utils::reqwest_client_builder()
+ .user_agent("Mozilla/5.0 (X11; Linux i686; rv:109.0) Gecko/20100101 Firefox/118.0")
+ .timeout(timeout)
+ .redirect(Policy::none())
+diff --git a/crates/store/Cargo.toml b/crates/store/Cargo.toml
+index 1f8965049..3bd1a228c 100644
+--- a/crates/store/Cargo.toml
++++ b/crates/store/Cargo.toml
+@@ -16,7 +16,7 @@ async-nats = { version = "0.40", default-features = false, features = ["server_2
+ azure_core = { version = "0.21.0", optional = true }
+ azure_storage = { version = "0.21.0", default-features = false, features = ["enable_reqwest_rustls", "hmac_rust"], optional = true }
+ azure_storage_blobs = { version = "0.21.0", default-features = false, features = ["enable_reqwest_rustls", "hmac_rust"], optional = true }
+-reqwest = { version = "0.12", default-features = false, features = ["rustls-tls-webpki-roots", "http2", "stream"]}
++reqwest = { version = "0.12", default-features = false, features = ["http2", "stream"]}
+ tokio = { version = "1.45", features = ["sync", "fs", "io-util"] }
+ r2d2 = { version = "0.8.10", optional = true }
+ futures = { version = "0.3", optional = true }
+diff --git a/crates/store/src/backend/azure/mod.rs b/crates/store/src/backend/azure/mod.rs
+index 8bbaea073..fbdb03eb7 100644
+--- a/crates/store/src/backend/azure/mod.rs
++++ b/crates/store/src/backend/azure/mod.rs
+@@ -63,7 +63,7 @@ impl AzureStore {
+ let timeout = config
+ .property_or_default::<Duration>((&prefix, "timeout"), "30s")
+ .unwrap_or_else(|| Duration::from_secs(30));
+- let transport = match reqwest::Client::builder().timeout(timeout).build() {
++ let transport = match utils::reqwest_client_builder().timeout(timeout).build() {
+ Ok(client) => Arc::new(client),
+ Err(err) => {
+ config.new_build_error(
+diff --git a/crates/store/src/backend/http/lookup.rs b/crates/store/src/backend/http/lookup.rs
+index dbaa932ed..ff7cca2d8 100644
+--- a/crates/store/src/backend/http/lookup.rs
++++ b/crates/store/src/backend/http/lookup.rs
+@@ -91,7 +91,7 @@ impl HttpStore {
+ async fn try_refresh(&self) -> trc::Result<AHashMap<String, Value<'static>>> {
+ let time = Instant::now();
+ let agent = BROWSER_USER_AGENTS.choose(&mut rand::rng()).unwrap();
+- let response = reqwest::Client::builder()
++ let response = utils::reqwest_client_builder()
+ .timeout(self.config.timeout)
+ .user_agent(*agent)
+ .build()
+diff --git a/crates/trc/Cargo.toml b/crates/trc/Cargo.toml
+index 61d1cd69a..22d8e6599 100644
+--- a/crates/trc/Cargo.toml
++++ b/crates/trc/Cargo.toml
+@@ -11,7 +11,7 @@ mail-parser = { version = "0.11", features = ["full_encoding"] }
+ base64 = "0.22.1"
+ serde = "1.0"
+ serde_json = "1.0.120"
+-reqwest = { version = "0.12", default-features = false, features = ["rustls-tls-webpki-roots", "http2"]}
++reqwest = { version = "0.12", default-features = false, features = ["http2"]}
+ rtrb = "0.3.1"
+ parking_lot = "0.12.3"
+ tokio = { version = "1.45", features = ["net", "macros"] }
+diff --git a/crates/utils/Cargo.toml b/crates/utils/Cargo.toml
+index 4766c55b5..f45780d24 100644
+--- a/crates/utils/Cargo.toml
++++ b/crates/utils/Cargo.toml
+@@ -9,6 +9,7 @@ trc = { path = "../trc" }
+ rustls = { version = "0.23.5", default-features = false, features = ["std", "ring", "tls12"] }
+ rustls-pemfile = "2.0"
+ rustls-pki-types = { version = "1" }
++rustls-platform-verifier = { version = "0.5.3", optional = true }
+ tokio = { version = "1.45", features = ["net", "macros", "signal"] }
+ tokio-rustls = { version = "0.26", default-features = false, features = ["ring", "tls12"] }
+ serde = { version = "1.0", features = ["derive"]}
+@@ -45,6 +46,7 @@ privdrop = "0.5.3"
+
+ [features]
+ test_mode = []
++tls-native-roots = ["dep:rustls-platform-verifier", "reqwest/rustls-tls-native-roots"]
+
+ [dev-dependencies]
+ tokio = { version = "1.45", features = ["full"] }
+diff --git a/crates/utils/src/lib.rs b/crates/utils/src/lib.rs
+index 2b73df148..2efcc96d7 100644
+--- a/crates/utils/src/lib.rs
++++ b/crates/utils/src/lib.rs
+@@ -21,14 +21,14 @@ use compact_str::ToCompactString;
+ use futures::StreamExt;
+ use reqwest::Response;
+ use rustls::{
+- ClientConfig, RootCertStore, SignatureScheme,
++ ClientConfig, SignatureScheme,
+ client::danger::{HandshakeSignatureValid, ServerCertVerified, ServerCertVerifier},
+ };
+-use rustls_pki_types::TrustAnchor;
+
+ pub use downcast_rs;
+ pub use erased_serde;
+
++pub static USER_AGENT: &str = "Stalwart/1.0.0";
+ pub const BLOB_HASH_LEN: usize = 32;
+
+ #[derive(
+@@ -294,20 +294,28 @@ pub async fn wait_for_shutdown() {
+ }
+
+ pub fn rustls_client_config(allow_invalid_certs: bool) -> ClientConfig {
++ #[cfg(feature = "tls-native-roots")]
++ use rustls_platform_verifier::BuilderVerifierExt;
++
+ let config = ClientConfig::builder();
+
+ if !allow_invalid_certs {
+- let mut root_cert_store = RootCertStore::empty();
+-
+- root_cert_store.extend(webpki_roots::TLS_SERVER_ROOTS.iter().map(|ta| TrustAnchor {
+- subject: ta.subject.clone(),
+- subject_public_key_info: ta.subject_public_key_info.clone(),
+- name_constraints: ta.name_constraints.clone(),
+- }));
+-
+- config
+- .with_root_certificates(root_cert_store)
+- .with_no_client_auth()
++ #[cfg(feature = "tls-native-roots")]
++ let config = config.with_platform_verifier();
++
++ #[cfg(not(feature = "tls-native-roots"))]
++ let config = config.with_root_certificates(
++ webpki_roots::TLS_SERVER_ROOTS
++ .iter()
++ .map(|ta| rustls_pki_types::TrustAnchor {
++ subject: ta.subject.clone(),
++ subject_public_key_info: ta.subject_public_key_info.clone(),
++ name_constraints: ta.name_constraints.clone(),
++ })
++ .collect::<rustls::RootCertStore>(),
++ );
++
++ config.with_no_client_auth()
+ } else {
+ config
+ .dangerous()
+@@ -316,6 +324,18 @@ pub fn rustls_client_config(allow_invalid_certs: bool) -> ClientConfig {
+ }
+ }
+
++pub fn reqwest_client_builder() -> reqwest::ClientBuilder {
++ let builder = reqwest::Client::builder();
++
++ #[cfg(feature = "tls-native-roots")]
++ let builder = builder.tls_built_in_native_certs(true);
++
++ #[cfg(not(feature = "tls-native-roots"))]
++ let builder = builder.tls_built_in_webpki_certs(true);
++
++ builder.user_agent(USER_AGENT)
++}
++
+ #[derive(Debug)]
+ struct DummyVerifier;
+
+diff --git a/crates/utils/src/suffixlist.rs b/crates/utils/src/suffixlist.rs
+index e0921abe3..b53126ae9 100644
+--- a/crates/utils/src/suffixlist.rs
++++ b/crates/utils/src/suffixlist.rs
+@@ -111,7 +111,12 @@ impl PublicSuffix {
+
+ for (idx, value) in values.into_iter().enumerate() {
+ let bytes = if value.starts_with("https://") || value.starts_with("http://") {
+- let result = match reqwest::get(&value).await {
++ let result = match crate::reqwest_client_builder()
++ .build()
++ .unwrap_or_default()
++ .get(&value)
++ .await
++ {
+ Ok(r) => {
+ if r.status().is_success() {
+ r.bytes().await
+diff --git a/tests/Cargo.toml b/tests/Cargo.toml
+index 386b8255a..68692e939 100644
+--- a/tests/Cargo.toml
++++ b/tests/Cargo.toml
+@@ -59,7 +59,7 @@ rayon = { version = "1.5.1" }
+ flate2 = { version = "1.0.17", features = ["zlib"], default-features = false }
+ serde = { version = "1.0", features = ["derive"]}
+ serde_json = "1.0"
+-reqwest = { version = "0.12", default-features = false, features = ["rustls-tls-webpki-roots", "multipart", "http2"]}
++reqwest = { version = "0.12", default-features = false, features = ["multipart", "http2"]}
+ bytes = "1.4.0"
+ futures = "0.3"
+ ece = "2.2"
+diff --git a/tests/src/jmap/auth_oauth.rs b/tests/src/jmap/auth_oauth.rs
+index 0d05d3a77..fff811fcf 100644
+--- a/tests/src/jmap/auth_oauth.rs
++++ b/tests/src/jmap/auth_oauth.rs
+@@ -411,7 +411,7 @@ async fn post_bytes(
+ auth_token: Option<&str>,
+ params: &AHashMap<String, String>,
+ ) -> Bytes {
+- let mut client = reqwest::Client::builder()
++ let mut client = utils::reqwest_client_builder()
+ .timeout(Duration::from_millis(500))
+ .danger_accept_invalid_certs(true)
+ .build()
+@@ -437,7 +437,7 @@ async fn post_json<D: DeserializeOwned>(
+ auth_token: Option<&str>,
+ body: &impl Serialize,
+ ) -> D {
+- let mut client = reqwest::Client::builder()
++ let mut client = utils::reqwest_client_builder()
+ .timeout(Duration::from_millis(500))
+ .danger_accept_invalid_certs(true)
+ .build()
+@@ -473,7 +473,7 @@ async fn post_with_auth<T: DeserializeOwned>(
+ }
+
+ async fn get_bytes(url: &str) -> Bytes {
+- reqwest::Client::builder()
++ utils::reqwest_client_builder()
+ .timeout(Duration::from_millis(500))
+ .danger_accept_invalid_certs(true)
+ .build()
+diff --git a/tests/src/jmap/mod.rs b/tests/src/jmap/mod.rs
+index 554026a00..ffd8d292a 100644
+--- a/tests/src/jmap/mod.rs
++++ b/tests/src/jmap/mod.rs
+@@ -419,7 +419,7 @@ pub async fn jmap_raw_request(body: impl AsRef<str>, username: &str, secret: &st
+ }"#;
+
+ String::from_utf8(
+- reqwest::Client::builder()
++ utils::reqwest_client_builder()
+ .danger_accept_invalid_certs(true)
+ .timeout(Duration::from_millis(1000))
+ .default_headers(headers)
+@@ -620,7 +620,7 @@ impl ManagementApi {
+ query: &str,
+ body: Option<String>,
+ ) -> Result<String, String> {
+- let mut request = reqwest::Client::builder()
++ let mut request = utils::reqwest_client_builder()
+ .timeout(Duration::from_millis(500))
+ .danger_accept_invalid_certs(true)
+ .build()
+diff --git a/tests/src/smtp/management/queue.rs b/tests/src/smtp/management/queue.rs
+index e86f55169..a0d144127 100644
+--- a/tests/src/smtp/management/queue.rs
++++ b/tests/src/smtp/management/queue.rs
+@@ -493,7 +493,7 @@ async fn manage_queue() {
+
+ // Test authentication error
+ assert_eq!(
+- reqwest::Client::builder()
++ utils::reqwest_client_builder()
+ .timeout(Duration::from_millis(500))
+ .danger_accept_invalid_certs(true)
+ .build()
+diff --git a/tests/src/webdav/mod.rs b/tests/src/webdav/mod.rs
+index 0ed8b1888..25aab71a4 100644
+--- a/tests/src/webdav/mod.rs
++++ b/tests/src/webdav/mod.rs
+@@ -302,7 +302,7 @@ impl DummyWebDavClient {
+ headers: impl IntoIterator<Item = (&'static str, &str)>,
+ body: impl Into<String>,
+ ) -> DavResponse {
+- let mut request = reqwest::Client::builder()
++ let mut request = utils::reqwest_client_builder()
+ .timeout(Duration::from_millis(500))
+ .danger_accept_invalid_certs(true)
+ .build()