Compare commits
6 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
dabc88a053 | ||
|
|
9cdefcea09 | ||
|
|
5748af4c98 | ||
|
|
3e9b1876d9 | ||
|
|
795bedae76 | ||
|
|
891dd521ab |
17
CHANGELOG.md
17
CHANGELOG.md
@@ -1,3 +1,20 @@
|
||||
<a name="v0.11.13"></a>
|
||||
### v0.11.13 (2025-02-17)
|
||||
|
||||
#### Features
|
||||
|
||||
* Add WASM support ([#1037], [#1042])
|
||||
* Add method to get the TLS verify result with BoringSSL ([#1039])
|
||||
|
||||
#### Bug fixes
|
||||
|
||||
* Synchronous pool shutdowns being arbitrarily delayed ([#1041])
|
||||
|
||||
[#1037]: https://github.com/lettre/lettre/pull/1037
|
||||
[#1039]: https://github.com/lettre/lettre/pull/1039
|
||||
[#1041]: https://github.com/lettre/lettre/pull/1041
|
||||
[#1042]: https://github.com/lettre/lettre/pull/1042
|
||||
|
||||
<a name="v0.11.12"></a>
|
||||
### v0.11.12 (2025-02-02)
|
||||
|
||||
|
||||
21
Cargo.lock
generated
21
Cargo.lock
generated
@@ -1189,7 +1189,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "lettre"
|
||||
version = "0.11.12"
|
||||
version = "0.11.13"
|
||||
dependencies = [
|
||||
"async-std",
|
||||
"async-trait",
|
||||
@@ -1233,6 +1233,7 @@ dependencies = [
|
||||
"url",
|
||||
"uuid",
|
||||
"walkdir",
|
||||
"web-time",
|
||||
"webpki-roots",
|
||||
]
|
||||
|
||||
@@ -1447,9 +1448,9 @@ checksum = "b410bbe7e14ab526a0e86877eb47c6996a2bd7746f027ba551028c925390e4e9"
|
||||
|
||||
[[package]]
|
||||
name = "openssl"
|
||||
version = "0.10.68"
|
||||
version = "0.10.70"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6174bc48f102d208783c2c84bf931bb75927a617866870de8a4ea85597f871f5"
|
||||
checksum = "61cfb4e166a8bb8c9b55c500bc2308550148ece889be90f609377e58140f42c6"
|
||||
dependencies = [
|
||||
"bitflags",
|
||||
"cfg-if",
|
||||
@@ -1479,9 +1480,9 @@ checksum = "ff011a302c396a5197692431fc1948019154afc178baf7d8e37367442a4601cf"
|
||||
|
||||
[[package]]
|
||||
name = "openssl-sys"
|
||||
version = "0.9.104"
|
||||
version = "0.9.105"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "45abf306cbf99debc8195b66b7346498d7b10c210de50418b5ccd7ceba08c741"
|
||||
checksum = "8b22d5b84be05a8d6947c7cb71f7c849aa0f112acd4bf51c2a7c1c988ac0a9dc"
|
||||
dependencies = [
|
||||
"cc",
|
||||
"libc",
|
||||
@@ -2442,6 +2443,16 @@ dependencies = [
|
||||
"wasm-bindgen",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "web-time"
|
||||
version = "1.1.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5a6580f308b1fad9207618087a65c04e7a10bc77e02c8e84e9b00dd4b12fa0bb"
|
||||
dependencies = [
|
||||
"js-sys",
|
||||
"wasm-bindgen",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "webpki-roots"
|
||||
version = "0.26.7"
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
[package]
|
||||
name = "lettre"
|
||||
# remember to update html_root_url and README.md (Cargo.toml example and deps.rs badge)
|
||||
version = "0.11.12"
|
||||
version = "0.11.13"
|
||||
description = "Email client"
|
||||
readme = "README.md"
|
||||
homepage = "https://lettre.rs"
|
||||
@@ -75,6 +75,9 @@ ed25519-dalek = { version = "2", optional = true }
|
||||
# email formats
|
||||
email_address = { version = "0.2.1", default-features = false }
|
||||
|
||||
# webtime for wasm support
|
||||
web-time = { version = "1.1.0", optional = true }
|
||||
|
||||
[dev-dependencies]
|
||||
pretty_assertions = "1"
|
||||
criterion = "0.5"
|
||||
@@ -122,6 +125,9 @@ tokio1-boring-tls = ["tokio1", "boring-tls", "dep:tokio1_boring"]
|
||||
|
||||
dkim = ["dep:base64", "dep:sha2", "dep:rsa", "dep:ed25519-dalek"]
|
||||
|
||||
# wasm support
|
||||
web = ["dep:web-time"]
|
||||
|
||||
[lints.rust]
|
||||
unexpected_cfgs = { level = "warn", check-cfg = ['cfg(lettre_ignore_tls_mismatch)'] }
|
||||
|
||||
|
||||
@@ -28,8 +28,8 @@
|
||||
</div>
|
||||
|
||||
<div align="center">
|
||||
<a href="https://deps.rs/crate/lettre/0.11.12">
|
||||
<img src="https://deps.rs/crate/lettre/0.11.12/status.svg"
|
||||
<a href="https://deps.rs/crate/lettre/0.11.13">
|
||||
<img src="https://deps.rs/crate/lettre/0.11.13/status.svg"
|
||||
alt="dependency status" />
|
||||
</a>
|
||||
</div>
|
||||
|
||||
@@ -107,6 +107,7 @@
|
||||
//! * **tracing**: Logging using the `tracing` crate
|
||||
//! * **mime03**: Allow creating a [`ContentType`] from an existing [mime 0.3] `Mime` struct
|
||||
//! * **dkim**: Add support for signing email with DKIM
|
||||
//! * **web**: WebAssembly support using the `web-time` crate for time operations
|
||||
//!
|
||||
//! [`SMTP`]: crate::transport::smtp
|
||||
//! [`sendmail`]: crate::transport::sendmail
|
||||
@@ -121,7 +122,7 @@
|
||||
//! [mime 0.3]: https://docs.rs/mime/0.3
|
||||
//! [DKIM]: https://datatracker.ietf.org/doc/html/rfc6376
|
||||
|
||||
#![doc(html_root_url = "https://docs.rs/crate/lettre/0.11.12")]
|
||||
#![doc(html_root_url = "https://docs.rs/crate/lettre/0.11.13")]
|
||||
#![doc(html_favicon_url = "https://lettre.rs/favicon.ico")]
|
||||
#![doc(html_logo_url = "https://avatars0.githubusercontent.com/u/15113230?v=4")]
|
||||
#![forbid(unsafe_code)]
|
||||
@@ -218,6 +219,7 @@ mod executor;
|
||||
#[cfg(feature = "builder")]
|
||||
#[cfg_attr(docsrs, doc(cfg(feature = "builder")))]
|
||||
pub mod message;
|
||||
mod time;
|
||||
pub mod transport;
|
||||
|
||||
use std::error::Error as StdError;
|
||||
|
||||
@@ -346,6 +346,9 @@ fn dkim_canonicalize_headers<'a>(
|
||||
/// Sign with Dkim a message by adding Dkim-Signature header created with configuration expressed by
|
||||
/// `dkim_config`
|
||||
pub fn dkim_sign(message: &mut Message, dkim_config: &DkimConfig) {
|
||||
#[cfg(feature = "web")]
|
||||
dkim_sign_fixed_time(message, dkim_config, crate::time::now());
|
||||
#[cfg(not(feature = "web"))]
|
||||
dkim_sign_fixed_time(message, dkim_config, SystemTime::now());
|
||||
}
|
||||
|
||||
|
||||
@@ -21,7 +21,7 @@ impl Date {
|
||||
///
|
||||
/// Shortcut for `Date::new(SystemTime::now())`
|
||||
pub fn now() -> Self {
|
||||
Self::new(SystemTime::now())
|
||||
Self::new(crate::time::now())
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -277,7 +277,7 @@ impl MessageBuilder {
|
||||
/// Shortcut for `self.date(SystemTime::now())`, it is automatically inserted
|
||||
/// if no date has been provided.
|
||||
pub fn date_now(self) -> Self {
|
||||
self.date(SystemTime::now())
|
||||
self.date(crate::time::now())
|
||||
}
|
||||
|
||||
/// Set or add mailbox to `ReplyTo` header
|
||||
|
||||
18
src/time.rs
Normal file
18
src/time.rs
Normal file
@@ -0,0 +1,18 @@
|
||||
use std::time::SystemTime;
|
||||
|
||||
#[cfg(feature = "web")]
|
||||
pub(crate) fn now() -> SystemTime {
|
||||
fn to_std_systemtime(time: web_time::SystemTime) -> std::time::SystemTime {
|
||||
let duration = time
|
||||
.duration_since(web_time::SystemTime::UNIX_EPOCH)
|
||||
.unwrap();
|
||||
SystemTime::UNIX_EPOCH + duration
|
||||
}
|
||||
|
||||
to_std_systemtime(web_time::SystemTime::now())
|
||||
}
|
||||
|
||||
#[cfg(not(feature = "web"))]
|
||||
pub(crate) fn now() -> SystemTime {
|
||||
SystemTime::now()
|
||||
}
|
||||
@@ -373,6 +373,22 @@ impl AsyncSmtpConnection {
|
||||
self.stream.get_ref().peer_certificate()
|
||||
}
|
||||
|
||||
/// Currently this is only avaialable when using Boring TLS and
|
||||
/// returns the result of the verification of the TLS certificate
|
||||
/// presented by the peer, if any. Only the last error encountered
|
||||
/// during verification is presented.
|
||||
/// It can be useful when you don't want to fail outright the TLS
|
||||
/// negotiation, for example when a self-signed certificate is
|
||||
/// encountered, but still want to record metrics or log the fact.
|
||||
/// When using DANE verification, the PKI root of trust moves from
|
||||
/// the CAs to DNS, so self-signed certificates are permitted as long
|
||||
/// as the TLSA records match the leaf or issuer certificates.
|
||||
/// It cannot be called on non Boring TLS streams.
|
||||
#[cfg(feature = "boring-tls")]
|
||||
pub fn tls_verify_result(&self) -> Result<(), Error> {
|
||||
self.stream.get_ref().tls_verify_result()
|
||||
}
|
||||
|
||||
/// All the X509 certificates of the chain (DER encoded)
|
||||
#[cfg(any(feature = "rustls-tls", feature = "boring-tls"))]
|
||||
pub fn certificate_chain(&self) -> Result<Vec<Vec<u8>>, Error> {
|
||||
|
||||
@@ -429,6 +429,30 @@ impl AsyncNetworkStream {
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "boring-tls")]
|
||||
pub fn tls_verify_result(&self) -> Result<(), Error> {
|
||||
match &self.inner {
|
||||
#[cfg(feature = "tokio1")]
|
||||
InnerAsyncNetworkStream::Tokio1Tcp(_) => {
|
||||
Err(error::client("Connection is not encrypted"))
|
||||
}
|
||||
#[cfg(feature = "tokio1-native-tls")]
|
||||
InnerAsyncNetworkStream::Tokio1NativeTls(_) => panic!("Unsupported"),
|
||||
#[cfg(feature = "tokio1-rustls-tls")]
|
||||
InnerAsyncNetworkStream::Tokio1RustlsTls(_) => panic!("Unsupported"),
|
||||
#[cfg(feature = "tokio1-boring-tls")]
|
||||
InnerAsyncNetworkStream::Tokio1BoringTls(stream) => {
|
||||
stream.ssl().verify_result().map_err(error::tls)
|
||||
}
|
||||
#[cfg(feature = "async-std1")]
|
||||
InnerAsyncNetworkStream::AsyncStd1Tcp(_) => {
|
||||
Err(error::client("Connection is not encrypted"))
|
||||
}
|
||||
#[cfg(feature = "async-std1-rustls-tls")]
|
||||
InnerAsyncNetworkStream::AsyncStd1RustlsTls(_) => panic!("Unsupported"),
|
||||
InnerAsyncNetworkStream::None => panic!("InnerNetworkStream::None must never be built"),
|
||||
}
|
||||
}
|
||||
pub fn certificate_chain(&self) -> Result<Vec<Vec<u8>>, Error> {
|
||||
match &self.inner {
|
||||
#[cfg(feature = "tokio1")]
|
||||
|
||||
@@ -308,6 +308,22 @@ impl SmtpConnection {
|
||||
self.stream.get_ref().peer_certificate()
|
||||
}
|
||||
|
||||
/// Currently this is only avaialable when using Boring TLS and
|
||||
/// returns the result of the verification of the TLS certificate
|
||||
/// presented by the peer, if any. Only the last error encountered
|
||||
/// during verification is presented.
|
||||
/// It can be useful when you don't want to fail outright the TLS
|
||||
/// negotiation, for example when a self-signed certificate is
|
||||
/// encountered, but still want to record metrics or log the fact.
|
||||
/// When using DANE verification, the PKI root of trust moves from
|
||||
/// the CAs to DNS, so self-signed certificates are permitted as long
|
||||
/// as the TLSA records match the leaf or issuer certificates.
|
||||
/// It cannot be called on non Boring TLS streams.
|
||||
#[cfg(feature = "boring-tls")]
|
||||
pub fn tls_verify_result(&self) -> Result<(), Error> {
|
||||
self.stream.get_ref().tls_verify_result()
|
||||
}
|
||||
|
||||
/// All the X509 certificates of the chain (DER encoded)
|
||||
#[cfg(any(feature = "rustls-tls", feature = "boring-tls"))]
|
||||
pub fn certificate_chain(&self) -> Result<Vec<Vec<u8>>, Error> {
|
||||
|
||||
@@ -222,6 +222,22 @@ impl NetworkStream {
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "boring-tls")]
|
||||
pub fn tls_verify_result(&self) -> Result<(), Error> {
|
||||
match &self.inner {
|
||||
InnerNetworkStream::Tcp(_) => Err(error::client("Connection is not encrypted")),
|
||||
#[cfg(feature = "native-tls")]
|
||||
InnerNetworkStream::NativeTls(_) => panic!("Unsupported"),
|
||||
#[cfg(feature = "rustls-tls")]
|
||||
InnerNetworkStream::RustlsTls(_) => panic!("Unsupported"),
|
||||
#[cfg(feature = "boring-tls")]
|
||||
InnerNetworkStream::BoringTls(stream) => {
|
||||
stream.ssl().verify_result().map_err(error::tls)
|
||||
}
|
||||
InnerNetworkStream::None => panic!("InnerNetworkStream::None must never be built"),
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(any(feature = "rustls-tls", feature = "boring-tls"))]
|
||||
pub fn certificate_chain(&self) -> Result<Vec<Vec<u8>>, Error> {
|
||||
match &self.inner {
|
||||
|
||||
@@ -109,6 +109,7 @@ impl Pool {
|
||||
}
|
||||
}
|
||||
|
||||
drop(pool);
|
||||
thread::sleep(idle_timeout);
|
||||
}
|
||||
})
|
||||
|
||||
Reference in New Issue
Block a user