diff --git a/Cargo.lock b/Cargo.lock index b9a2b923..7fa40ad7 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -255,9 +255,9 @@ dependencies = [ [[package]] name = "async-nats" -version = "0.47.0" +version = "0.49.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "07d6f157065c3461096d51aacde0c326fa49f3f6e0199e204c566842cdaa5299" +checksum = "407486109ea5cfdf53fde05f46996dadf0547518a4d49f050d25f405ae31ed2d" dependencies = [ "aws-lc-rs", "bytes", @@ -265,7 +265,7 @@ dependencies = [ "memchr", "pin-project", "portable-atomic", - "rand 0.8.6", + "rand 0.10.1", "regex", "rustls-native-certs", "rustls-pki-types", @@ -273,7 +273,7 @@ dependencies = [ "serde", "serde_json", "serde_repr", - "thiserror 1.0.69", + "thiserror 2.0.18", "tokio", "tokio-rustls", "tokio-stream", @@ -724,6 +724,15 @@ dependencies = [ "generic-array 0.14.7", ] +[[package]] +name = "block-padding" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "710f1dd022ef4e93f8a438b4ba958de7f64308434fa6a87104481645cc30068b" +dependencies = [ + "hybrid-array", +] + [[package]] name = "blowfish" version = "0.7.0" @@ -897,6 +906,15 @@ dependencies = [ "cipher 0.4.4", ] +[[package]] +name = "cbc" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ce2dc9ee5f88d11e0beb842c88b33c8a5cf0d1329c4b19494af42b07dbfe8896" +dependencies = [ + "cipher 0.5.2", +] + [[package]] name = "cc" version = "1.2.62" @@ -1134,7 +1152,7 @@ dependencies = [ "proxy-header", "psl", "pwhash", - "quick-xml 0.39.4", + "quick-xml 0.40.1", "quick_cache", "rasn", "rasn-cms", @@ -1664,7 +1682,7 @@ dependencies = [ "hashify", "hyper", "mail-parser", - "quick-xml 0.39.4", + "quick-xml 0.40.1", "rkyv", "serde", "serde_json", @@ -2098,10 +2116,10 @@ dependencies = [ name = "email" version = "0.16.8" dependencies = [ - "aes 0.8.4", + "aes 0.9.1", "aes-gcm", "aes-gcm-siv", - "cbc", + "cbc 0.2.1", "common", "compact_str", "directory", @@ -2993,7 +3011,7 @@ dependencies = [ "mail-parser", "mime", "pkcs8", - "quick-xml 0.39.4", + "quick-xml 0.40.1", "registry", "rkyv", "rsa", @@ -3457,7 +3475,7 @@ version = "0.1.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "879f10e63c20629ecabbb64a8010319738c66a5cd0c29b02d63d272b03751d01" dependencies = [ - "block-padding", + "block-padding 0.3.3", "generic-array 0.14.7", ] @@ -3467,6 +3485,7 @@ version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4250ce6452e92010fdf7268ccc5d14faa80bb12fc741938534c58f16804e03c7" dependencies = [ + "block-padding 0.4.2", "hybrid-array", ] @@ -4016,9 +4035,9 @@ dependencies = [ [[package]] name = "libsqlite3-sys" -version = "0.37.0" +version = "0.38.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b1f111c8c41e7c61a49cd34e44c7619462967221a6443b0ec299e0ac30cfb9b1" +checksum = "a76001fb4daed01e5f2b518aac0b4dc592e7c734da63dbffcf0c64fa612a8d0c" dependencies = [ "cc", "pkg-config", @@ -5561,6 +5580,15 @@ dependencies = [ "serde", ] +[[package]] +name = "quick-xml" +version = "0.40.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2474bd2e5029e7ccb6abb2ba48cf2383a333851dedf495901544281590c7da7f" +dependencies = [ + "memchr", +] + [[package]] name = "quick_cache" version = "0.6.22" @@ -6301,9 +6329,9 @@ checksum = "4ade083ccbb4bf536df69d1f6432cc23deb7acccff86b183f3923a6fd56a1153" [[package]] name = "rusqlite" -version = "0.39.0" +version = "0.40.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a0d2b0146dd9661bf67bb107c0bb2a55064d556eeb3fc314151b957f313bcd4e" +checksum = "1b3492ea85308705c3a5cc24fb9b9cf77273d30590349070db42991202b214c4" dependencies = [ "bitflags", "fallible-iterator 0.3.0", @@ -6733,12 +6761,12 @@ dependencies = [ "anyhow", "argon2", "base64 0.22.1", - "block-padding", + "block-padding 0.3.3", "blowfish 0.9.1", "buffered-reader", "camellia", "cast5", - "cbc", + "cbc 0.1.2", "cfb-mode", "chrono", "cipher 0.4.4", @@ -7706,7 +7734,7 @@ dependencies = [ "nlp", "num_cpus", "pop3", - "quick-xml 0.39.4", + "quick-xml 0.40.1", "rayon", "registry", "reqwest 0.13.4", diff --git a/crates/common/Cargo.toml b/crates/common/Cargo.toml index e172e514..4e2d96ac 100644 --- a/crates/common/Cargo.toml +++ b/crates/common/Cargo.toml @@ -85,7 +85,7 @@ rasn-pkix = "0.28" sequoia-openpgp = { version = "2.0", default-features = false, features = ["crypto-rust", "allow-experimental-crypto", "allow-variable-time-crypto"] } zxcvbn = "3.1.0" pkcs8 = { version = "0.10.2", features = ["alloc", "std"] } -quick-xml = "0.39" +quick-xml = "0.40" [target.'cfg(unix)'.dependencies] privdrop = "0.5.3" diff --git a/crates/common/src/network/autoconfig/autodiscover.rs b/crates/common/src/network/autoconfig/autodiscover.rs index 8727fc82..c548a483 100644 --- a/crates/common/src/network/autoconfig/autodiscover.rs +++ b/crates/common/src/network/autoconfig/autodiscover.rs @@ -6,6 +6,7 @@ use crate::{Server, manager::application::Resource}; use quick_xml::Reader; +use quick_xml::XmlVersion; use quick_xml::events::Event; use registry::schema::enums::ServiceProtocol; use std::fmt::Write; @@ -170,7 +171,7 @@ fn parse_autodiscover_request(bytes: &[u8]) -> Result { } if let Ok(Event::Text(text)) = reader.read_event_into(&mut buf) - && let Ok(text) = text.xml_content() + && let Ok(text) = text.xml_content(XmlVersion::Implicit1_0) && text.contains('@') { return Ok(text.trim().to_lowercase()); diff --git a/crates/coordinator/Cargo.toml b/crates/coordinator/Cargo.toml index 9a525f53..e3e212fc 100644 --- a/crates/coordinator/Cargo.toml +++ b/crates/coordinator/Cargo.toml @@ -10,13 +10,13 @@ registry = { path = "../registry" } trc = { path = "../trc" } futures = { version = "0.3", optional = true } tokio = { version = "1.47", features = ["sync", "fs", "io-util"] } -async-nats = { version = "0.47", default-features = false, features = ["server_2_10", "server_2_11", "aws-lc-rs"], optional = true } +async-nats = { version = "0.49", default-features = false, features = ["server_2_10", "server_2_11", "aws-lc-rs"], optional = true } zenoh = { version = "1.3.4", default-features = false, features = ["auth_pubkey", "transport_multilink", "transport_compression", "transport_quic", "transport_tcp", "transport_tls", "transport_udp"], optional = true } rdkafka = { version = "0.39", features = ["cmake-build"], optional = true } redis = { version = "1.1", features = [ "tokio-comp", "tokio-rustls-comp", "tls-rustls-insecure", "tls-rustls", "cluster-async"], optional = true } [features] -nats = ["async-nats"] +nats = ["async-nats", "futures"] zenoh = ["dep:zenoh"] kafka = ["rdkafka"] redis = ["dep:redis", "futures"] diff --git a/crates/dav-proto/Cargo.toml b/crates/dav-proto/Cargo.toml index b30ff4c7..ed4ce04b 100644 --- a/crates/dav-proto/Cargo.toml +++ b/crates/dav-proto/Cargo.toml @@ -7,7 +7,7 @@ edition = "2024" trc = { path = "../trc" } types = { path = "../types" } hashify = "0.2.6" -quick-xml = { version = "0.39" } +quick-xml = "0.40" calcard = { version = "0.3", features = ["rkyv"] } mail-parser = { version = "0.11", features = ["full_encoding", "rkyv"] } hyper = "1.6.0" diff --git a/crates/dav-proto/src/parser/tokenizer.rs b/crates/dav-proto/src/parser/tokenizer.rs index 9ba9697a..72870873 100644 --- a/crates/dav-proto/src/parser/tokenizer.rs +++ b/crates/dav-proto/src/parser/tokenizer.rs @@ -7,7 +7,7 @@ use super::{Error, RawElement, Token, UnexpectedToken, XmlValueParser}; use crate::schema::{Attribute, AttributeValue, Element, NamedElement, Namespace}; use quick_xml::{ - NsReader, + NsReader, XmlVersion, events::{Event, attributes::AttrError}, name::ResolveResult, }; @@ -46,7 +46,7 @@ impl<'x> Tokenizer<'x> { } Event::Text(text) if text.iter().any(|ch| !ch.is_ascii_whitespace()) => { return text - .xml_content() + .xml_content(XmlVersion::Implicit1_0) .map(Token::Text) .map_err(|err| Error::Xml(Box::new(err.into()))); } @@ -66,7 +66,7 @@ impl<'x> Tokenizer<'x> { ); return entity - .xml_content() + .xml_content(XmlVersion::Implicit1_0) .map(Token::Text) .map_err(|err| Error::Xml(Box::new(err.into()))); } @@ -282,7 +282,7 @@ impl RawElement<'_> { &self, ) -> impl Iterator>> + '_ { self.element.attributes().filter_map(|attr| match attr { - Ok(attr) => match attr.unescape_value() { + Ok(attr) => match attr.normalized_value(XmlVersion::Implicit1_0) { Ok(value) => Attribute::from_param(attr.key.as_ref(), value).map(Ok), Err(err) => Some(Err(err.into())), }, diff --git a/crates/email/Cargo.toml b/crates/email/Cargo.toml index 230e4a1f..69ba53f1 100644 --- a/crates/email/Cargo.toml +++ b/crates/email/Cargo.toml @@ -21,10 +21,10 @@ sieve-rs = { version = "0.7", features = ["rkyv"] } tokio = { version = "1.47", features = ["net", "macros"] } serde = { version = "1.0", features = ["derive"]} serde_json = "1.0" -aes = "0.8.3" +aes = "0.9" aes-gcm = "0.10.1" aes-gcm-siv = "0.11.1" -cbc = { version = "0.1.2", features = ["alloc"] } +cbc = { version = "0.2", features = ["alloc"] } rasn = "0.28" rasn-cms = "0.28" rasn-pkix = "0.28" diff --git a/crates/email/src/message/crypto.rs b/crates/email/src/message/crypto.rs index bb55e6fc..2b90520e 100644 --- a/crates/email/src/message/crypto.rs +++ b/crates/email/src/message/crypto.rs @@ -4,7 +4,7 @@ * SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-SEL */ -use aes::cipher::{BlockEncryptMut, KeyIvInit, block_padding::Pkcs7}; +use aes::cipher::{BlockModeEncrypt, KeyIvInit, block_padding::Pkcs7}; use common::auth::{ ACCOUNT_FLAG_ENCRYPT_ALGO_AES256, ACCOUNT_FLAG_ENCRYPT_METHOD_PGP, ACCOUNT_FLAG_ENCRYPT_TRAIN_SPAM_FILTER, EncryptionKeys, @@ -390,11 +390,13 @@ impl EncryptionFlags for u64 { fn encrypt(&self, key: &[u8], iv: &[u8], contents: &[u8]) -> Vec { if *self & ACCOUNT_FLAG_ENCRYPT_ALGO_AES256 != 0 { - cbc::Encryptor::::new(key.into(), iv.into()) - .encrypt_padded_vec_mut::(contents) + cbc::Encryptor::::new_from_slices(key, iv) + .expect("invalid key or iv length") + .encrypt_padded_vec::(contents) } else { - cbc::Encryptor::::new(key.into(), iv.into()) - .encrypt_padded_vec_mut::(contents) + cbc::Encryptor::::new_from_slices(key, iv) + .expect("invalid key or iv length") + .encrypt_padded_vec::(contents) } } diff --git a/crates/http/Cargo.toml b/crates/http/Cargo.toml index 684eca9c..42fe9a8b 100644 --- a/crates/http/Cargo.toml +++ b/crates/http/Cargo.toml @@ -29,7 +29,7 @@ hyper = { version = "1.0.1", features = ["server", "http1", "http2"] } hyper-util = { version = "0.1.1", features = ["tokio"] } http-body-util = "0.1.0" async-stream = "0.3.5" -quick-xml = "0.39" +quick-xml = "0.40" serde = { version = "1.0", features = ["derive"]} serde_json = "1.0" x509-parser = "0.18" diff --git a/crates/jmap-proto/src/error/request.rs b/crates/jmap-proto/src/error/request.rs index e0f39114..9474bf49 100644 --- a/crates/jmap-proto/src/error/request.rs +++ b/crates/jmap-proto/src/error/request.rs @@ -379,7 +379,9 @@ mod tests { fn rate_limit_headers_match_spec() { // Spec example: RateLimit-Policy: "burst";q=100;w=60,"daily";q=1000;w=86400 let mut p1 = String::new(); - RateLimitPolicy::new("burst", 100).with_window(60).fmt_policy(&mut p1); + RateLimitPolicy::new("burst", 100) + .with_window(60) + .fmt_policy(&mut p1); assert_eq!(p1, r#""burst";q=100;w=60"#); // Spec example: RateLimit-Policy: "peruser";q=65535;qu="content-bytes";w=10 @@ -392,13 +394,24 @@ mod tests { // Spec example: RateLimit: "default";r=50;t=30 let mut s = String::new(); - RateLimitPolicy::new("default", 100).with_remaining(50).with_reset(30).fmt_state(&mut s); + RateLimitPolicy::new("default", 100) + .with_remaining(50) + .with_reset(30) + .fmt_state(&mut s); assert_eq!(s, r#""default";r=50;t=30"#); // Two policies in one header let err = RequestError::too_many_requests() - .with_rate_limit(RateLimitPolicy::new("burst", 100).with_window(60).with_reset(30)) - .with_rate_limit(RateLimitPolicy::new("daily", 1000).with_window(86400).with_reset(3600)); + .with_rate_limit( + RateLimitPolicy::new("burst", 100) + .with_window(60) + .with_reset(30), + ) + .with_rate_limit( + RateLimitPolicy::new("daily", 1000) + .with_window(86400) + .with_reset(3600), + ); assert_eq!( err.rate_limit_policy_header().as_deref(), Some(r#""burst";q=100;w=60, "daily";q=1000;w=86400"#), @@ -410,4 +423,3 @@ mod tests { assert_eq!(err.retry_after, Some(3600)); } } - diff --git a/crates/services/src/task_manager/dkim.rs b/crates/services/src/task_manager/dkim.rs index d88e75d0..093bdae8 100644 --- a/crates/services/src/task_manager/dkim.rs +++ b/crates/services/src/task_manager/dkim.rs @@ -386,12 +386,7 @@ async fn dkim_management(server: &Server, task: &TaskDomainManagement) -> trc::R let record = generate_dkim_dns_record_name(&signature.object, &domain.name); if let Some((updater, origin)) = &dns_updater { match updater - .set_rrset( - origin, - &record, - dns_update::DnsRecordType::TXT, - Vec::new(), - ) + .set_rrset(origin, &record, dns_update::DnsRecordType::TXT, Vec::new()) .await { Ok(_) => { diff --git a/crates/store/Cargo.toml b/crates/store/Cargo.toml index e15756dd..ae579e3a 100644 --- a/crates/store/Cargo.toml +++ b/crates/store/Cargo.toml @@ -11,7 +11,7 @@ trc = { path = "../trc" } registry = { path = "../registry" } rocksdb = { version = "0.24", optional = true, features = ["multi-threaded-cf"] } foundationdb = { version = "0.10", features = ["embedded-fdb-include", "fdb-7_4"], optional = true } -rusqlite = { version = "0.39", features = ["bundled"], optional = true } +rusqlite = { version = "0.40", features = ["bundled"], optional = true } rust-s3 = { version = "0.37", default-features = false, features = ["tokio-rustls-tls"], optional = true } 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 } diff --git a/crates/store/src/backend/redis/mod.rs b/crates/store/src/backend/redis/mod.rs index 108e538e..087b5025 100644 --- a/crates/store/src/backend/redis/mod.rs +++ b/crates/store/src/backend/redis/mod.rs @@ -12,6 +12,7 @@ use deadpool::{ use redis::{ Client, ProtocolVersion, cluster::{ClusterClient, ClusterClientBuilder}, + cluster_read_routing::RandomReplicaStrategy, }; use registry::{ schema::{enums::RedisProtocol, structs}, @@ -77,7 +78,7 @@ impl RedisStore { builder = builder.min_retry_wait(value.as_millis()); } if config.read_from_replicas { - builder = builder.read_from_replicas(); + builder = builder.read_routing_strategy(RandomReplicaStrategy); } if matches!(config.protocol_version, RedisProtocol::Resp3) { builder = builder.use_protocol(ProtocolVersion::RESP3); diff --git a/tests/Cargo.toml b/tests/Cargo.toml index 045af822..762aacf8 100644 --- a/tests/Cargo.toml +++ b/tests/Cargo.toml @@ -79,7 +79,7 @@ biscuit = "0.8.0" form_urlencoded = "1.1.0" rkyv = { version = "0.8.10", features = ["little_endian"] } compact_str = "0.9.0" -quick-xml = "0.39" +quick-xml = "0.40" jmap-tools = { version = "0.1" } dns-update = { version = "0.5", features = ["test_provider"] } x509-parser = "0.18" diff --git a/tests/src/system/quota.rs b/tests/src/system/quota.rs index 167f8b45..3628f0e5 100644 --- a/tests/src/system/quota.rs +++ b/tests/src/system/quota.rs @@ -104,11 +104,8 @@ pub async fn test(test: &mut TestServer) { // Test temporary blob quota (3 files) DISABLE_UPLOAD_QUOTA.store(false, std::sync::atomic::Ordering::Relaxed); let client = account.jmap_client().await; - let raw_http = HttpRequest::with_credentials( - 8899, - "user1@example.org", - "this is a very strong password1", - ); + let raw_http = + HttpRequest::with_credentials(8899, "user1@example.org", "this is a very strong password1"); let upload_url = format!("/jmap/upload/{account_id}"); for i in 0..3 { assert_eq!( diff --git a/tests/src/system/security.rs b/tests/src/system/security.rs index 171aee6f..b85a92f7 100644 --- a/tests/src/system/security.rs +++ b/tests/src/system/security.rs @@ -272,11 +272,8 @@ pub async fn test(test: &mut TestServer) { // Concurrent requests check let client = Arc::new(client); - let raw_http = HttpRequest::with_credentials( - 8899, - "user@example.org", - "this is a very strong password", - ); + let raw_http = + HttpRequest::with_credentials(8899, "user@example.org", "this is a very strong password"); for _ in 0..8 { let client_ = client.clone(); tokio::spawn(async move { diff --git a/tests/src/utils/webdav.rs b/tests/src/utils/webdav.rs index 60f53744..d0d8ef33 100644 --- a/tests/src/utils/webdav.rs +++ b/tests/src/utils/webdav.rs @@ -13,7 +13,7 @@ use dav_proto::{ }; use groupware::DavResourceName; use hyper::{HeaderMap, Method, StatusCode, header::AUTHORIZATION}; -use quick_xml::{Reader, events::Event}; +use quick_xml::{Reader, XmlVersion, events::Event}; use std::{borrow::Cow, time::Duration}; use store::rand::{Rng, distr::Alphanumeric, rng}; @@ -1136,7 +1136,7 @@ fn flatten_xml(xml: &str) -> Vec<(String, String)> { for attr in e.attributes() { let attr = attr.unwrap(); let key = str::from_utf8(attr.key.as_ref()).unwrap().to_string(); - let value = attr.unescape_value().unwrap(); + let value = attr.normalized_value(XmlVersion::Implicit1_0).unwrap(); let value_str = value.trim().to_string(); result.push((format!("{}.[{}]", base_path, key), value_str)); @@ -1151,7 +1151,7 @@ fn flatten_xml(xml: &str) -> Vec<(String, String)> { for attr in e.attributes() { let attr = attr.unwrap(); let key = str::from_utf8(attr.key.as_ref()).unwrap().to_string(); - let value = attr.unescape_value().unwrap(); + let value = attr.normalized_value(XmlVersion::Implicit1_0).unwrap(); let value_str = value.trim().to_string(); has_attrs = true; result.push((format!("{}.[{}]", base_path, key), value_str)); @@ -1162,7 +1162,7 @@ fn flatten_xml(xml: &str) -> Vec<(String, String)> { } } Event::Text(e) => { - let text = e.xml_content().unwrap(); + let text = e.xml_content(XmlVersion::Implicit1_0).unwrap(); let trimmed = text.trim(); if !trimmed.is_empty() { if let Some(text_content) = text_content.as_mut() {