diff --git a/Cargo.lock b/Cargo.lock index c9e9cea3..1727856a 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -187,9 +187,9 @@ dependencies = [ [[package]] name = "anyhow" -version = "1.0.95" +version = "1.0.96" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "34ac096ce696dc2fcabef30516bb13c0a68a11d30131d3df6f04711467681b04" +checksum = "6b964d184e89d9b6b67dd2715bc8e74cf3107fb2b529990c90cf517326150bf4" [[package]] name = "arbitrary" @@ -291,7 +291,7 @@ checksum = "965c2d33e53cb6b267e148a4cb0760bc01f4904c1cd4bb4002a085bb016d1490" dependencies = [ "proc-macro2", "quote", - "syn 2.0.96", + "syn 2.0.98", "synstructure 0.13.1", ] @@ -314,7 +314,7 @@ checksum = "7b18050c2cd6fe86c3a76584ef5e0baf286d038cda203eb6223df2cc413565f7" dependencies = [ "proc-macro2", "quote", - "syn 2.0.96", + "syn 2.0.98", ] [[package]] @@ -330,9 +330,9 @@ dependencies = [ [[package]] name = "async-compression" -version = "0.4.18" +version = "0.4.20" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "df895a515f70646414f4b45c0b79082783b80552b373a68283012928df56f522" +checksum = "310c9bcae737a48ef5cdee3174184e6d548b292739ede61a1f955ef76a738861" dependencies = [ "flate2", "futures-core", @@ -360,7 +360,7 @@ checksum = "3b43422f69d8ff38f95f1b2bb76517c91589a924d1559a0e935d7c8ce0274c11" dependencies = [ "proc-macro2", "quote", - "syn 2.0.96", + "syn 2.0.98", ] [[package]] @@ -382,18 +382,18 @@ checksum = "c7c24de15d275a1ecfd47a380fb4d5ec9bfe0933f309ed5e705b775596a3574d" dependencies = [ "proc-macro2", "quote", - "syn 2.0.96", + "syn 2.0.98", ] [[package]] name = "async-trait" -version = "0.1.85" +version = "0.1.86" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3f934833b4b7233644e5848f235df3f57ed8c80f1528a26c3dfa13d2147fa056" +checksum = "644dd749086bf3771a2fbc5f256fdb982d53f011c7d5d560304eafeecebce79d" dependencies = [ "proc-macro2", "quote", - "syn 2.0.96", + "syn 2.0.98", ] [[package]] @@ -404,17 +404,17 @@ checksum = "1505bd5d3d116872e7271a6d4e16d81d0c8570876c8de68093a09ac269d8aac0" [[package]] name = "attohttpc" -version = "0.28.2" +version = "0.28.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "412b79ce053cef36eda52c25664b45ec92a21769488e20d5a8bf0b3c9e1a28cb" +checksum = "07a9b245ba0739fc90935094c29adbaee3f977218b5fb95e822e261cda7f56a3" dependencies = [ "http 1.2.0", "log", - "rustls 0.23.21", + "rustls 0.23.23", "serde", "serde_json", "url", - "webpki-roots 0.26.7", + "webpki-roots 0.26.8", ] [[package]] @@ -667,7 +667,7 @@ version = "0.69.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "271383c67ccabffb7381723dea0672a673f292304fcb45c01cc648c7a8d58088" dependencies = [ - "bitflags 2.8.0", + "bitflags 2.9.0", "cexpr", "clang-sys", "itertools 0.12.1", @@ -678,7 +678,7 @@ dependencies = [ "regex", "rustc-hash 1.1.0", "shlex", - "syn 2.0.96", + "syn 2.0.98", ] [[package]] @@ -687,7 +687,7 @@ version = "0.70.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f49d8fed880d473ea71efb9bf597651e77201bdd4893efe54c9e5d65ae04ce6f" dependencies = [ - "bitflags 2.8.0", + "bitflags 2.9.0", "cexpr", "clang-sys", "itertools 0.13.0", @@ -698,7 +698,7 @@ dependencies = [ "regex", "rustc-hash 1.1.0", "shlex", - "syn 2.0.96", + "syn 2.0.98", ] [[package]] @@ -707,16 +707,16 @@ version = "0.71.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5f58bf3d7db68cfbac37cfc485a8d711e87e064c3d0fe0435b92f7a407f9d6b3" dependencies = [ - "bitflags 2.8.0", + "bitflags 2.9.0", "cexpr", "clang-sys", "itertools 0.13.0", "proc-macro2", "quote", "regex", - "rustc-hash 2.1.0", + "rustc-hash 2.1.1", "shlex", - "syn 2.0.96", + "syn 2.0.98", ] [[package]] @@ -730,7 +730,7 @@ dependencies = [ "num-bigint", "num-traits", "once_cell", - "ring 0.17.8", + "ring 0.17.11", "serde", "serde_json", ] @@ -773,9 +773,9 @@ checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" [[package]] name = "bitflags" -version = "2.8.0" +version = "2.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8f68f53c83ab957f72c32642f3868eec03eb974d1fb82e453128456482613d36" +checksum = "5c8214115b7bf84099f1309324e63141d4c5d7cc26862f97a0a857dbefe165bd" [[package]] name = "bitpacking" @@ -819,9 +819,9 @@ dependencies = [ [[package]] name = "blake3" -version = "1.5.5" +version = "1.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b8ee0c1824c4dea5b5f81736aff91bae041d2c07ee1192bec91054e10e3e601e" +checksum = "675f87afced0413c9bb02843499dbbd3882a237645883f71a2b59644a6d2f753" dependencies = [ "arrayref", "arrayvec", @@ -898,7 +898,7 @@ dependencies = [ "proc-macro-crate", "proc-macro2", "quote", - "syn 2.0.96", + "syn 2.0.98", ] [[package]] @@ -912,19 +912,18 @@ dependencies = [ [[package]] name = "buffered-reader" -version = "1.3.2" +version = "1.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fabd1c5e55587a8e8526172d63ad2ba665fa18c8acb39ec9a77af1708c982b9b" +checksum = "db26bf1f092fd5e05b5ab3be2f290915aeb6f3f20c4e9f86ce0f07f336c2412f" dependencies = [ - "lazy_static", "libc", ] [[package]] name = "bumpalo" -version = "3.16.0" +version = "3.17.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "79296716171880943b8470b5f8d03aa55eb2e645a4874bdbb28adb49162e012c" +checksum = "1628fb46dfa0b37568d12e5edd512553eccf6a22a78e8bde00bb4aed84d5bdbf" [[package]] name = "bytecheck" @@ -968,7 +967,7 @@ checksum = "efb7846e0cb180355c2dec69e721edafa36919850f1a9f52ffba4ebc0393cb71" dependencies = [ "proc-macro2", "quote", - "syn 2.0.96", + "syn 2.0.98", ] [[package]] @@ -985,28 +984,26 @@ checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b" [[package]] name = "bytes" -version = "1.9.0" +version = "1.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "325918d6fe32f23b19878fe4b34794ae41fc19ddbe53b10571a4874d44ffd39b" +checksum = "f61dac84819c6588b558454b194026eb1f09c293b9036ae9b159e74e73ab6cf9" [[package]] name = "bzip2" -version = "0.4.4" +version = "0.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bdb116a6ef3f6c3698828873ad02c3014b3c85cadb88496095628e3ef1e347f8" +checksum = "49ecfb22d906f800d4fe833b6282cf4dc1c298f5057ca0b5445e5c209735ca47" dependencies = [ "bzip2-sys", - "libc", ] [[package]] name = "bzip2-sys" -version = "0.1.11+1.0.8" +version = "0.1.13+1.0.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "736a955f3fa7875102d57c82b8cac37ec45224a07fd32d58f9f7a186b6cd4cdc" +checksum = "225bff33b2141874fe80d71e07d6eec4f85c5c216453dd96388240f96e1acc14" dependencies = [ "cc", - "libc", "pkg-config", ] @@ -1049,9 +1046,9 @@ dependencies = [ [[package]] name = "cc" -version = "1.2.10" +version = "1.2.16" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "13208fcbb66eaeffe09b99fffbe1af420f00a7b35aa99ad683dfc1aa76145229" +checksum = "be714c154be609ec7f5dad223a33bf1482fff90472de28f7362806e6d4832b8c" dependencies = [ "jobserver", "libc", @@ -1110,9 +1107,9 @@ checksum = "613afe47fcd5fac7ccf1db93babcb082c5994d996f20b8b159f2ad1658eb5724" [[package]] name = "chrono" -version = "0.4.39" +version = "0.4.40" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7e36cc9d416881d2e24f9a963be5fb1cd90966419ac844274161d10488b3e825" +checksum = "1a7964611d71df112cb1730f2ee67324fcf4d0fc6606acbbe9bfe06df124637c" dependencies = [ "android-tzdata", "iana-time-zone", @@ -1120,7 +1117,7 @@ dependencies = [ "num-traits", "serde", "wasm-bindgen", - "windows-targets 0.52.6", + "windows-link", ] [[package]] @@ -1156,9 +1153,9 @@ dependencies = [ [[package]] name = "clap" -version = "4.5.27" +version = "4.5.31" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "769b0145982b4b48713e01ec42d61614425f27b7058bda7180a3a41f30104796" +checksum = "027bb0d98429ae334a8698531da7077bdf906419543a35a55c2cb1b66437d767" dependencies = [ "clap_builder", "clap_derive", @@ -1166,9 +1163,9 @@ dependencies = [ [[package]] name = "clap_builder" -version = "4.5.27" +version = "4.5.31" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1b26884eb4b57140e4d2d93652abfa49498b938b3c9179f9fc487b0acc3edad7" +checksum = "5589e0cba072e0f3d23791efac0fd8627b49c829c196a492e88168e6a669d863" dependencies = [ "anstream", "anstyle", @@ -1178,14 +1175,14 @@ dependencies = [ [[package]] name = "clap_derive" -version = "4.5.24" +version = "4.5.28" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "54b755194d6389280185988721fffba69495eed5ee9feeee9a599b53db80318c" +checksum = "bf4ced95c6f4a675af3da73304b9ac4ed991640c36374e4b46795c49e17cf1ed" dependencies = [ "heck 0.5.0", "proc-macro2", "quote", - "syn 2.0.96", + "syn 2.0.98", ] [[package]] @@ -1207,9 +1204,9 @@ dependencies = [ [[package]] name = "cmake" -version = "0.1.53" +version = "0.1.54" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e24a03c8b52922d68a1589ad61032f2c1aa5a8158d2aa0d93c6e9534944bbad6" +checksum = "e7caa3f9de89ddbe2c607f4101924c5abec803763ae9534e4f4d7d8f84aa81f0" dependencies = [ "cc", ] @@ -1281,10 +1278,10 @@ dependencies = [ "rcgen 0.12.1", "regex", "reqwest 0.12.12", - "ring 0.17.8", + "ring 0.17.11", "rkyv 0.8.10", "rsa", - "rustls 0.23.21", + "rustls 0.23.23", "rustls-pemfile 2.2.0", "rustls-pki-types", "serde", @@ -1295,7 +1292,7 @@ dependencies = [ "smtp-proto", "store", "tokio", - "tokio-rustls 0.26.1", + "tokio-rustls 0.26.2", "trc", "unicode-security", "utils", @@ -1544,9 +1541,9 @@ dependencies = [ [[package]] name = "csv-core" -version = "0.1.11" +version = "0.1.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5efa2b3d7902f4b634a20cae3c9c4e6209dc4779feb6863329607560143efa70" +checksum = "7d02f3b0da4c6504f86e9cd789d8dbafab48c2321be74e9987593de5a894d93d" dependencies = [ "memchr", ] @@ -1584,7 +1581,7 @@ checksum = "f46882e17999c6cc590af592290432be3bce0428cb0d5f8b6715e4dc7b383eb3" dependencies = [ "proc-macro2", "quote", - "syn 2.0.96", + "syn 2.0.98", ] [[package]] @@ -1632,7 +1629,7 @@ dependencies = [ "proc-macro2", "quote", "strsim 0.11.1", - "syn 2.0.96", + "syn 2.0.98", ] [[package]] @@ -1654,7 +1651,7 @@ checksum = "d336a2a514f6ccccaa3e09b02d41d35330c07ddf03a62165fcec10bb561c7806" dependencies = [ "darling_core 0.20.10", "quote", - "syn 2.0.96", + "syn 2.0.98", ] [[package]] @@ -1665,9 +1662,9 @@ checksum = "04d2cd9c18b9f454ed67da600630b021a8a80bf33f8c95896ab33aaf1c26b728" [[package]] name = "data-encoding" -version = "2.7.0" +version = "2.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0e60eed09d8c01d3cee5b7d30acb059b76614c918fa0f992e0dd6eeb10daad6f" +checksum = "575f75dfd25738df5b91b8e43e14d44bda14637a58fae779fd2b064f8bf3e010" [[package]] name = "dbl" @@ -1692,9 +1689,9 @@ dependencies = [ [[package]] name = "deadpool" -version = "0.12.1" +version = "0.12.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6541a3916932fe57768d4be0b1ffb5ec7cbf74ca8c903fdfd5c0fe8aa958f0ed" +checksum = "5ed5957ff93768adf7a65ab167a17835c3d2c3c50d084fe305174c112f468e2f" dependencies = [ "deadpool-runtime", "num_cpus", @@ -1708,7 +1705,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3d697d376cbfa018c23eb4caab1fd1883dd9c906a8c034e8d9a3cb06a7e0bef9" dependencies = [ "async-trait", - "deadpool 0.12.1", + "deadpool 0.12.2", "getrandom 0.2.15", "tokio", "tokio-postgres", @@ -1798,7 +1795,7 @@ checksum = "30542c1ad912e0e3d22a1935c290e12e8a29d704a420177a31faad4a601a0800" dependencies = [ "proc-macro2", "quote", - "syn 2.0.96", + "syn 2.0.98", ] [[package]] @@ -1853,7 +1850,7 @@ dependencies = [ "pwhash", "regex", "reqwest 0.12.12", - "rustls 0.23.21", + "rustls 0.23.23", "rustls-pki-types", "scrypt", "serde", @@ -1863,7 +1860,7 @@ dependencies = [ "smtp-proto", "store", "tokio", - "tokio-rustls 0.26.1", + "tokio-rustls 0.26.2", "totp-rs", "trc", "utils", @@ -1898,7 +1895,7 @@ checksum = "97369cbbc041bc366949bc74d34658d6cda5621039731c6310521892a3a20ae0" dependencies = [ "proc-macro2", "quote", - "syn 2.0.96", + "syn 2.0.98", ] [[package]] @@ -1954,9 +1951,9 @@ dependencies = [ [[package]] name = "dyn-clone" -version = "1.0.17" +version = "1.0.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0d6ef0072f8a535281e4876be788938b528e9a1d43900b82c2569af7da799125" +checksum = "feeef44e73baff3a26d371801df019877a9866a8c493d315ab00177843314f35" [[package]] name = "eax" @@ -2039,9 +2036,9 @@ dependencies = [ [[package]] name = "either" -version = "1.13.0" +version = "1.14.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "60b1af1c220855b6ceac025d3f6ecdd2b7c4894bfe9cd9bda4fbb4bc7c0d4cf0" +checksum = "b7914353092ddf589ad78f25c5c1c21b7f80b0ff8621e7c814c3485b5306da9d" [[package]] name = "elasticsearch" @@ -2157,14 +2154,14 @@ dependencies = [ "heck 0.5.0", "proc-macro2", "quote", - "syn 2.0.96", + "syn 2.0.98", ] [[package]] name = "equivalent" -version = "1.0.1" +version = "1.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5443807d6dff69373d433ab9ef5378ad8df50ca6298caf15de6e52e24aaf54d5" +checksum = "877a4ace8713b0bcf2a4e7eec82529c029f1d0619886d18145fea96c3ffe5c0f" [[package]] name = "erased-serde" @@ -2219,7 +2216,7 @@ version = "0.11.8" dependencies = [ "proc-macro2", "quote", - "syn 2.0.96", + "syn 2.0.98", ] [[package]] @@ -2296,9 +2293,9 @@ checksum = "0ce7134b9999ecaf8bcd65542e436736ef32ddca1b3e06094cb6ec5755203b80" [[package]] name = "flate2" -version = "1.0.35" +version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c936bfdafb507ebbf50b8074c54fa31c5be9a1e7e5f467dd659697041407d07c" +checksum = "11faaf5a5236997af9848be0bef4db95824b1d534ebc64d0f0c6cf3e67bd38dc" dependencies = [ "crc32fast", "libz-sys", @@ -2396,7 +2393,7 @@ checksum = "9be610412e5a92d89855fb15b099a57792b7dbdcf8ac74c5a0e24d9b7b1b6f7f" dependencies = [ "proc-macro2", "quote", - "syn 2.0.96", + "syn 2.0.98", "try_map", ] @@ -2449,7 +2446,7 @@ checksum = "e99b8b3c28ae0e84b604c75f721c21dc77afb3706076af5e8216d15fd1deaae3" dependencies = [ "frunk_proc_macro_helpers", "quote", - "syn 2.0.96", + "syn 2.0.98", ] [[package]] @@ -2461,7 +2458,7 @@ dependencies = [ "frunk_core", "proc-macro2", "quote", - "syn 2.0.96", + "syn 2.0.98", ] [[package]] @@ -2473,7 +2470,7 @@ dependencies = [ "frunk_core", "frunk_proc_macro_helpers", "quote", - "syn 2.0.96", + "syn 2.0.98", ] [[package]] @@ -2553,7 +2550,7 @@ checksum = "162ee34ebcb7c64a8abebc059ce0fee27c2262618d7b60ed8faf72fef13c3650" dependencies = [ "proc-macro2", "quote", - "syn 2.0.96", + "syn 2.0.98", ] [[package]] @@ -2738,9 +2735,9 @@ dependencies = [ [[package]] name = "h2" -version = "0.4.7" +version = "0.4.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ccae279728d634d083c00f6099cb58f01cc99c145b84b8be2f6c74618d79922e" +checksum = "5017294ff4bb30944501348f6f8e42e6ad28f42c8bbef7a74029aff064a4e3c2" dependencies = [ "atomic-waker", "bytes", @@ -2793,7 +2790,7 @@ checksum = "f208758247e68e239acaa059e72e4ce1f30f2a4b6523f19c1b923d25b7e9cceb" dependencies = [ "proc-macro2", "quote", - "syn 2.0.96", + "syn 2.0.98", ] [[package]] @@ -2837,9 +2834,9 @@ checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70" [[package]] name = "hickory-client" -version = "0.24.2" +version = "0.24.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "949d2fef0bbdd31a0f6affc6bf390b4a0017492903eff6f7516cb382d9e85536" +checksum = "156579a5cd8d1fc6f0df87cc21b6ee870db978a163a1ba484acd98a4eff5a6de" dependencies = [ "cfg-if", "data-encoding", @@ -2857,9 +2854,9 @@ dependencies = [ [[package]] name = "hickory-proto" -version = "0.24.2" +version = "0.24.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "447afdcdb8afb9d0a852af6dc65d9b285ce720ed7a59e42a8bf2e931c67bc1b5" +checksum = "92652067c9ce6f66ce53cc38d1169daa36e6e7eb7dd3b63b5103bd9d97117248" dependencies = [ "async-trait", "bytes", @@ -2875,7 +2872,7 @@ dependencies = [ "ipnet", "once_cell", "rand 0.8.5", - "ring 0.16.20", + "ring 0.17.11", "rustls 0.21.12", "rustls-pemfile 1.0.4", "thiserror 1.0.69", @@ -2888,9 +2885,9 @@ dependencies = [ [[package]] name = "hickory-resolver" -version = "0.24.2" +version = "0.24.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0a2e2aba9c389ce5267d31cf1e4dace82390ae276b0b364ea55630b1fa1b44b4" +checksum = "cbb117a1ca520e111743ab2f6688eddee69db4e0ea242545a604dce8a66fd22e" dependencies = [ "cfg-if", "futures-util", @@ -3095,7 +3092,7 @@ dependencies = [ "bytes", "futures-channel", "futures-util", - "h2 0.4.7", + "h2 0.4.8", "http 1.2.0", "http-body 1.0.1", "httparse", @@ -3131,12 +3128,12 @@ dependencies = [ "http 1.2.0", "hyper 1.6.0", "hyper-util", - "rustls 0.23.21", + "rustls 0.23.23", "rustls-pki-types", "tokio", - "tokio-rustls 0.26.1", + "tokio-rustls 0.26.2", "tower-service", - "webpki-roots 0.26.7", + "webpki-roots 0.26.8", ] [[package]] @@ -3309,7 +3306,7 @@ checksum = "1ec89e9337638ecdc08744df490b221a7399bf8d164eb52a665454e60e075ad6" dependencies = [ "proc-macro2", "quote", - "syn 2.0.96", + "syn 2.0.98", ] [[package]] @@ -3370,11 +3367,11 @@ dependencies = [ "nlp", "parking_lot", "rand 0.9.0", - "rustls 0.23.21", + "rustls 0.23.23", "rustls-pemfile 2.2.0", "store", "tokio", - "tokio-rustls 0.26.1", + "tokio-rustls 0.26.2", "trc", "utils", ] @@ -3413,7 +3410,7 @@ dependencies = [ "libflate", "proc-macro2", "quote", - "syn 2.0.96", + "syn 2.0.98", ] [[package]] @@ -3466,9 +3463,9 @@ dependencies = [ [[package]] name = "inout" -version = "0.1.3" +version = "0.1.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a0c10553d664a4d0bcff9f4215d0aac67a639cc68ef660840afe309b807bc9f5" +checksum = "879f10e63c20629ecabbb64a8010319738c66a5cd0c29b02d63d272b03751d01" dependencies = [ "block-padding", "generic-array 0.14.7", @@ -3554,6 +3551,15 @@ dependencies = [ "either", ] +[[package]] +name = "itertools" +version = "0.14.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2b192c782037fadd9cfa75548310488aabdbf3d2da73885b31bd0abd03351285" +dependencies = [ + "either", +] + [[package]] name = "itoa" version = "1.0.14" @@ -3639,6 +3645,7 @@ dependencies = [ "rand 0.9.0", "reqwest 0.12.12", "rev_lines", + "rkyv 0.8.10", "rsa", "serde", "serde_json", @@ -3650,9 +3657,9 @@ dependencies = [ "spam-filter", "store", "tokio", - "tokio-tungstenite 0.26.1", + "tokio-tungstenite 0.26.2", "trc", - "tungstenite 0.26.1", + "tungstenite 0.26.2", "utils", "x509-parser 0.16.0", ] @@ -3828,9 +3835,9 @@ dependencies = [ [[package]] name = "libc" -version = "0.2.169" +version = "0.2.170" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b5aba8db14291edd000dfcc4d620c7ebfb122c613afb886ca8803fa4e128a20a" +checksum = "875b3680cb2f8f71bdcf9a30f38d48282f5d3c95cbf9b3fa57269bb5d5c06828" [[package]] name = "libflate" @@ -3878,7 +3885,7 @@ version = "0.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c0ff37bd590ca25063e35af745c343cb7a0271906fb7b37e4813e8f79f00268d" dependencies = [ - "bitflags 2.8.0", + "bitflags 2.9.0", "libc", ] @@ -3933,9 +3940,9 @@ checksum = "d26c52dbd32dccf2d10cac7725f8eae5296885fb5703b261f7d0a0739ec807ab" [[package]] name = "litemap" -version = "0.7.4" +version = "0.7.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4ee93343901ab17bd981295f2cf0026d4ad018c7c31ba84549a4ddbb47a45104" +checksum = "23fb14cb19457329c82206317a5663005a4d404783dc74f4252769b0d5f42856" [[package]] name = "lock_api" @@ -3955,9 +3962,9 @@ checksum = "9374ef4228402d4b7e403e5838cb880d9ee663314b0a900d5a6aabf0c213552e" [[package]] name = "log" -version = "0.4.25" +version = "0.4.26" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "04cbf5b083de1c7e0222a7a51dbfdba1cbe1c6ab0b15e29fff3f6c077fd9cd9f" +checksum = "30bde2b3dc3671ae49d8e2e9f044c7c005836e7a023ee57cffa25ab82764bb9e" [[package]] name = "lru" @@ -4017,7 +4024,7 @@ dependencies = [ "quick-xml 0.37.2", "quick_cache", "rand 0.8.5", - "ring 0.17.8", + "ring 0.17.11", "rsa", "rustls-pemfile 2.2.0", "serde", @@ -4054,12 +4061,12 @@ dependencies = [ "base64 0.22.1", "gethostname 0.5.0", "md5", - "rustls 0.23.21", + "rustls 0.23.23", "rustls-pki-types", "smtp-proto", "tokio", - "tokio-rustls 0.26.1", - "webpki-roots 0.26.7", + "tokio-rustls 0.26.2", + "webpki-roots 0.26.8", ] [[package]] @@ -4099,12 +4106,12 @@ dependencies = [ "mail-send", "md5", "parking_lot", - "rustls 0.23.21", + "rustls 0.23.23", "rustls-pemfile 2.2.0", "sieve-rs", "store", "tokio", - "tokio-rustls 0.26.1", + "tokio-rustls 0.26.2", "trc", "utils", ] @@ -4135,7 +4142,7 @@ checksum = "5cf92c10c7e361d6b99666ec1c6f9805b0bea2c3bd8c78dc6fe98ac5bd78db11" dependencies = [ "proc-macro2", "quote", - "syn 2.0.96", + "syn 2.0.98", ] [[package]] @@ -4220,9 +4227,9 @@ checksum = "68354c5c6bd36d73ff3feceb05efa59b6acb7626617f4962be322a825e61f79a" [[package]] name = "miniz_oxide" -version = "0.8.3" +version = "0.8.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b8402cab7aefae129c6977bb0ff1b8fd9a04eb5b51efc50a70bea51cda0c7924" +checksum = "8e3e04debbb59698c15bacbb6d93584a8c0ca9cc3213cb423d31f760d8843ce5" dependencies = [ "adler2", ] @@ -4241,22 +4248,22 @@ dependencies = [ [[package]] name = "munge" -version = "0.4.2" +version = "0.4.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8743b8dfaf66acac79aca9ff2440e8680fef745b6260e6a31d1772b14cfa2862" +checksum = "a0091202c98cf06da46c279fdf50cccb6b1c43b4521abdf6a27b4c7e71d5d9d7" dependencies = [ "munge_macro", ] [[package]] name = "munge_macro" -version = "0.4.2" +version = "0.4.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "66191390a55bb9830fa8468c12634442ea4199c6e390ddf08ddcace35b3cd5da" +checksum = "734799cf91479720b2f970c61a22850940dd91e27d4f02b1c6fc792778df2459" dependencies = [ "proc-macro2", "quote", - "syn 2.0.96", + "syn 2.0.98", ] [[package]] @@ -4272,7 +4279,7 @@ dependencies = [ "proc-macro-error2", "proc-macro2", "quote", - "syn 2.0.96", + "syn 2.0.98", "termcolor", "thiserror 1.0.69", ] @@ -4311,7 +4318,7 @@ dependencies = [ "twox-hash", "url", "webpki", - "webpki-roots 0.26.7", + "webpki-roots 0.26.8", ] [[package]] @@ -4323,7 +4330,7 @@ dependencies = [ "base64 0.21.7", "bigdecimal", "bindgen 0.71.1", - "bitflags 2.8.0", + "bitflags 2.9.0", "bitvec", "btoi", "byteorder", @@ -4398,7 +4405,7 @@ dependencies = [ "radix_trie", "rust-stemmers", "serde", - "siphasher 1.0.1", + "siphasher", "tokio", "utils", "whatlang", @@ -4529,9 +4536,9 @@ dependencies = [ [[package]] name = "once_cell" -version = "1.20.2" +version = "1.20.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1261fe7e33c73b354eab43b1273a57c8f967d0391e80353e51f764ac02cf6775" +checksum = "945462a4b81e43c4e3ba96bd7b49d834c6f61198356aa858733bc4acf3cbe62e" [[package]] name = "opaque-debug" @@ -4541,11 +4548,11 @@ checksum = "c08d65885ee38876c4f86fa503fb49d7b507c2b62552df7c70b2fce627e06381" [[package]] name = "openssl" -version = "0.10.69" +version = "0.10.71" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f5e534d133a060a3c19daec1eb3e98ec6f4685978834f2dbadfe2ec215bab64e" +checksum = "5e14130c6a98cd258fdcb0fb6d744152343ff729cbfcb28c656a9d12b999fbcd" dependencies = [ - "bitflags 2.8.0", + "bitflags 2.9.0", "cfg-if", "foreign-types", "libc", @@ -4562,7 +4569,7 @@ checksum = "a948666b637a0f465e8564c73e89d4dde00d72d4d473cc972f390fc3dcee7d9c" dependencies = [ "proc-macro2", "quote", - "syn 2.0.96", + "syn 2.0.98", ] [[package]] @@ -4573,9 +4580,9 @@ checksum = "d05e27ee213611ffe7d6348b942e8f942b37114c00cc03cec254295a4a17852e" [[package]] name = "openssl-sys" -version = "0.9.104" +version = "0.9.106" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "45abf306cbf99debc8195b66b7346498d7b10c210de50418b5ccd7ceba08c741" +checksum = "8bb61ea9811cc39e3c2069f40b8b8e2e70d8569b361f879786cc7ed48b777cdd" dependencies = [ "cc", "libc", @@ -4691,9 +4698,9 @@ dependencies = [ [[package]] name = "p384" -version = "0.13.0" +version = "0.13.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "70786f51bcc69f6a4c0360e063a4cac5419ef7c5cd5b3c99ad70f3be5ba79209" +checksum = "fe42f1670a52a47d448f14b6a5c61dd78fce51856e68edaa38f7ae3a46b8d6b6" dependencies = [ "ecdsa", "elliptic-curve", @@ -4775,9 +4782,9 @@ dependencies = [ [[package]] name = "pem" -version = "3.0.4" +version = "3.0.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8e459365e590736a54c3fa561947c84837534b8e9af6fc5bf781307e82658fae" +checksum = "38af38e8470ac9dee3ce1bae1af9c1671fffc44ddfd8bd1d0a3445bf349a8ef3" dependencies = [ "base64 0.22.1", "serde", @@ -4814,7 +4821,7 @@ version = "0.11.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1fd6780a80ae0c52cc120a26a1a42c1ae51b247a253e4e06113d23d2c2edd078" dependencies = [ - "phf_shared 0.11.3", + "phf_shared", ] [[package]] @@ -4824,7 +4831,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "aef8048c789fa5e851558d709946d6d79a8ff88c0440c587967f8e94bfb1216a" dependencies = [ "phf_generator", - "phf_shared 0.11.3", + "phf_shared", ] [[package]] @@ -4833,46 +4840,37 @@ version = "0.11.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3c80231409c20246a13fddb31776fb942c38553c51e871f8cbd687a4cfb5843d" dependencies = [ - "phf_shared 0.11.3", + "phf_shared", "rand 0.8.5", ] -[[package]] -name = "phf_shared" -version = "0.10.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b6796ad771acdc0123d2a88dc428b5e38ef24456743ddb1744ed628f9815c096" -dependencies = [ - "siphasher 0.3.11", -] - [[package]] name = "phf_shared" version = "0.11.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "67eabc2ef2a60eb7faa00097bd1ffdb5bd28e62bf39990626a582201b7a754e5" dependencies = [ - "siphasher 1.0.1", + "siphasher", ] [[package]] name = "pin-project" -version = "1.1.8" +version = "1.1.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1e2ec53ad785f4d35dac0adea7f7dc6f1bb277ad84a680c7afefeae05d1f5916" +checksum = "dfe2e71e1471fe07709406bf725f710b02927c9c54b2b5b2ec0e8087d97c327d" dependencies = [ "pin-project-internal", ] [[package]] name = "pin-project-internal" -version = "1.1.8" +version = "1.1.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d56a66c0c55993aa927429d0f8a0abfd74f084e4d9c192cffed01e418d83eefb" +checksum = "f6e859e6e5bd50440ab63c47e3ebabc90f26251f7c73c3d3e837b74a1cc3fa67" dependencies = [ "proc-macro2", "quote", - "syn 2.0.96", + "syn 2.0.98", ] [[package]] @@ -4937,25 +4935,25 @@ dependencies = [ "jmap_proto", "mail-parser", "mail-send", - "rustls 0.23.21", + "rustls 0.23.23", "store", "tokio", - "tokio-rustls 0.26.1", + "tokio-rustls 0.26.2", "trc", "utils", ] [[package]] name = "portable-atomic" -version = "1.10.0" +version = "1.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "280dc24453071f1b63954171985a0b0d30058d287960968b9b2aca264c8d4ee6" +checksum = "350e9b48cbc6b0e028b0473b114454c6316e57336ee184ceab6e53f72c178b3e" [[package]] name = "postgres-protocol" -version = "0.6.7" +version = "0.6.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "acda0ebdebc28befa84bee35e651e4c5f09073d668c7aed4cf7e23c3cda84b23" +checksum = "76ff0abab4a9b844b93ef7b81f1efc0a366062aaef2cd702c76256b5dc075c54" dependencies = [ "base64 0.22.1", "byteorder", @@ -4964,16 +4962,16 @@ dependencies = [ "hmac 0.12.1", "md-5 0.10.6", "memchr", - "rand 0.8.5", + "rand 0.9.0", "sha2 0.10.8", "stringprep", ] [[package]] name = "postgres-types" -version = "0.2.8" +version = "0.2.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f66ea23a2d0e5734297357705193335e0a957696f34bed2f2faefacb2fec336f" +checksum = "613283563cd90e1dfc3518d548caee47e0e725455ed619881f5cf21f36de4b48" dependencies = [ "bytes", "fallible-iterator 0.2.0", @@ -5008,7 +5006,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6924ced06e1f7dfe3fa48d57b9f74f55d8915f5036121bef647ef4b204895fac" dependencies = [ "proc-macro2", - "syn 2.0.96", + "syn 2.0.98", ] [[package]] @@ -5072,7 +5070,7 @@ dependencies = [ "proc-macro-error-attr2", "proc-macro2", "quote", - "syn 2.0.96", + "syn 2.0.98", ] [[package]] @@ -5090,7 +5088,7 @@ version = "0.11.8" dependencies = [ "proc-macro2", "quote", - "syn 2.0.96", + "syn 2.0.98", ] [[package]] @@ -5109,9 +5107,9 @@ dependencies = [ [[package]] name = "prost" -version = "0.13.4" +version = "0.13.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2c0fef6c4230e4ccf618a35c59d7ede15dea37de8427500f50aff708806e42ec" +checksum = "2796faa41db3ec313a31f7624d9286acf277b52de526150b7e69f3debf891ee5" dependencies = [ "bytes", "prost-derive", @@ -5119,15 +5117,15 @@ dependencies = [ [[package]] name = "prost-derive" -version = "0.13.4" +version = "0.13.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "157c5a9d7ea5c2ed2d9fb8f495b64759f7816c7eaea54ba3978f0d63000162e3" +checksum = "8a56d757972c98b346a9b766e3f02746cde6dd1cd1d1d563472929fdd74bec4d" dependencies = [ "anyhow", - "itertools 0.13.0", + "itertools 0.14.0", "proc-macro2", "quote", - "syn 2.0.96", + "syn 2.0.98", ] [[package]] @@ -5142,9 +5140,9 @@ dependencies = [ [[package]] name = "psl" -version = "2.1.81" +version = "2.1.89" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5871e872678223987b84739333bf13e42f0c1fb102e30bba8dcdf1340d0bbcc9" +checksum = "d69a99af0dfed3b5279a24891a8128d536f075bef423b5974d70edd17e1f6c91" dependencies = [ "psl-types", ] @@ -5192,7 +5190,7 @@ checksum = "ca414edb151b4c8d125c12566ab0d74dc9cdba36fb80eb7b848c15f495fd32d1" dependencies = [ "proc-macro2", "quote", - "syn 2.0.96", + "syn 2.0.98", ] [[package]] @@ -5247,9 +5245,9 @@ dependencies = [ [[package]] name = "quick_cache" -version = "0.6.9" +version = "0.6.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7d7c94f8935a9df96bb6380e8592c70edf497a643f94bd23b2f76b399385dbf4" +checksum = "3f67cfc9c723c39f3615eb0840b00c4cb9e2b068d2fa761a30d845ec91730a59" dependencies = [ "ahash 0.8.11", "equivalent", @@ -5267,8 +5265,8 @@ dependencies = [ "pin-project-lite", "quinn-proto", "quinn-udp", - "rustc-hash 2.1.0", - "rustls 0.23.21", + "rustc-hash 2.1.1", + "rustls 0.23.23", "socket2", "thiserror 2.0.11", "tokio", @@ -5284,9 +5282,9 @@ dependencies = [ "bytes", "getrandom 0.2.15", "rand 0.8.5", - "ring 0.17.8", - "rustc-hash 2.1.0", - "rustls 0.23.21", + "ring 0.17.11", + "rustc-hash 2.1.1", + "rustls 0.23.23", "rustls-pki-types", "slab", "thiserror 2.0.11", @@ -5297,9 +5295,9 @@ dependencies = [ [[package]] name = "quinn-udp" -version = "0.5.9" +version = "0.5.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1c40286217b4ba3a71d644d752e6a0b71f13f1b6a2c5311acfcbe0c2418ed904" +checksum = "e46f3055866785f6b92bc6164b76be02ca8f2eb4b002c0354b28cf4c119e5944" dependencies = [ "cfg_aliases", "libc", @@ -5385,8 +5383,8 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3779b94aeb87e8bd4e834cee3650289ee9e0d5677f976ecdb6d219e5f4f6cd94" dependencies = [ "rand_chacha 0.9.0", - "rand_core 0.9.1", - "zerocopy 0.8.20", + "rand_core 0.9.3", + "zerocopy 0.8.21", ] [[package]] @@ -5416,7 +5414,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d3022b5f1df60f26e1ffddd6c66e8aa15de382ae63b3a0c1bfc0e4d3e3f325cb" dependencies = [ "ppv-lite86", - "rand_core 0.9.1", + "rand_core 0.9.3", ] [[package]] @@ -5439,12 +5437,11 @@ dependencies = [ [[package]] name = "rand_core" -version = "0.9.1" +version = "0.9.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a88e0da7a2c97baa202165137c158d0a2e824ac465d13d81046727b34cb247d3" +checksum = "99d9a13982dcf210057a8a78572b2217b667c3beacbf3a0d8b454f6f82837d38" dependencies = [ "getrandom 0.3.1", - "zerocopy 0.8.20", ] [[package]] @@ -5539,7 +5536,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "48406db8ac1f3cbc7dcdb56ec355343817958a356ff430259bb07baf7607e1e1" dependencies = [ "pem", - "ring 0.17.8", + "ring 0.17.11", "time", "yasna", ] @@ -5551,7 +5548,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "75e669e5202259b5314d1ea5397316ad400819437857b90861765f24c4cf80a2" dependencies = [ "pem", - "ring 0.17.8", + "ring 0.17.11", "rustls-pki-types", "time", "yasna", @@ -5575,25 +5572,25 @@ dependencies = [ "percent-encoding", "pin-project-lite", "rand 0.9.0", - "rustls 0.23.21", + "rustls 0.23.23", "rustls-native-certs 0.7.3", "ryu", "sha1_smol", "socket2", "tokio", - "tokio-rustls 0.26.1", + "tokio-rustls 0.26.2", "tokio-util", "url", - "webpki-roots 0.26.7", + "webpki-roots 0.26.8", ] [[package]] name = "redox_syscall" -version = "0.5.8" +version = "0.5.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "03a862b389f93e68874fbf580b9de08dd02facb9a788ebadaf4a3fd33cf58834" +checksum = "82b568323e98e49e2a0899dcee453dd679fae22d69adf9b11dd508d1549b7e2f" dependencies = [ - "bitflags 2.8.0", + "bitflags 2.9.0", ] [[package]] @@ -5709,7 +5706,7 @@ dependencies = [ "futures-channel", "futures-core", "futures-util", - "h2 0.4.7", + "h2 0.4.8", "http 1.2.0", "http-body 1.0.1", "http-body-util", @@ -5725,7 +5722,7 @@ dependencies = [ "percent-encoding", "pin-project-lite", "quinn", - "rustls 0.23.21", + "rustls 0.23.23", "rustls-pemfile 2.2.0", "rustls-pki-types", "serde", @@ -5733,7 +5730,7 @@ dependencies = [ "serde_urlencoded", "sync_wrapper 1.0.2", "tokio", - "tokio-rustls 0.26.1", + "tokio-rustls 0.26.2", "tokio-util", "tower 0.5.2", "tower-service", @@ -5742,7 +5739,7 @@ dependencies = [ "wasm-bindgen-futures", "wasm-streams", "web-sys", - "webpki-roots 0.26.7", + "webpki-roots 0.26.8", "windows-registry", ] @@ -5792,15 +5789,14 @@ dependencies = [ [[package]] name = "ring" -version = "0.17.8" +version = "0.17.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c17fa4cb658e3583423e915b9f3acc01cceaee1860e33d59ebae66adc3a2dc0d" +checksum = "da5349ae27d3887ca812fb375b45a4fbb36d8d12d2df394968cd86e35683fe73" dependencies = [ "cc", "cfg-if", "getrandom 0.2.15", "libc", - "spin 0.9.8", "untrusted 0.9.0", "windows-sys 0.52.0", ] @@ -5870,7 +5866,7 @@ checksum = "246b40ac189af6c675d124b802e8ef6d5246c53e17367ce9501f8f66a81abb7a" dependencies = [ "proc-macro2", "quote", - "syn 2.0.96", + "syn 2.0.98", ] [[package]] @@ -5952,7 +5948,7 @@ version = "0.33.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1c6d5e5acb6f6129fe3f7ba0a7fc77bca1942cb568535e18e7bc40262baf3110" dependencies = [ - "bitflags 2.8.0", + "bitflags 2.9.0", "fallible-iterator 0.3.0", "fallible-streaming-iterator", "hashlink", @@ -6048,9 +6044,9 @@ checksum = "08d43f7aa6b08d49f382cde6a7982047c3426db949b1424bc4b7ec9ae12c6ce2" [[package]] name = "rustc-hash" -version = "2.1.0" +version = "2.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c7fb8039b3032c191086b10f11f319a6e99e1e82889c5cc6046f515c9db1d497" +checksum = "357703d41365b4b27c590e3ed91eabb1b663f07c4c084095e60cbed4362dff0d" [[package]] name = "rustc_version" @@ -6085,7 +6081,7 @@ version = "0.38.44" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fdb5bc1ae2baa591800df16c9ca78619bf65c0488b41b96ccec5d11220d8c154" dependencies = [ - "bitflags 2.8.0", + "bitflags 2.9.0", "errno", "libc", "linux-raw-sys", @@ -6099,7 +6095,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3f56a14d1f48b391359b22f731fd4bd7e43c97f3c50eee276f3aa09c94784d3e" dependencies = [ "log", - "ring 0.17.8", + "ring 0.17.11", "rustls-webpki 0.101.7", "sct", ] @@ -6111,7 +6107,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bf4ef73721ac7bcd79b2b315da7779d8fc09718c6b3d2d1b2d94850eb8c18432" dependencies = [ "log", - "ring 0.17.8", + "ring 0.17.11", "rustls-pki-types", "rustls-webpki 0.102.8", "subtle", @@ -6120,12 +6116,12 @@ dependencies = [ [[package]] name = "rustls" -version = "0.23.21" +version = "0.23.23" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8f287924602bf649d949c63dc8ac8b235fa5387d394020705b80c4eb597ce5b8" +checksum = "47796c98c480fce5406ef69d1c76378375492c3b0a0de587be0c1d9feb12f395" dependencies = [ "once_cell", - "ring 0.17.8", + "ring 0.17.11", "rustls-pki-types", "rustls-webpki 0.102.8", "subtle", @@ -6190,7 +6186,7 @@ version = "0.101.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8b6275d1ee7a1cd780b64aca7726599a1dbc893b1e64144529e55c3c2f745765" dependencies = [ - "ring 0.17.8", + "ring 0.17.11", "untrusted 0.9.0", ] @@ -6200,7 +6196,7 @@ version = "0.102.8" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "64ca1bc8749bd4cf37b5ce386cc146580777b4e8572c7b97baf22c83f444bee9" dependencies = [ - "ring 0.17.8", + "ring 0.17.11", "rustls-pki-types", "untrusted 0.9.0", ] @@ -6243,9 +6239,9 @@ checksum = "ece8e78b2f38ec51c51f5d475df0a7187ba5111b2a28bdc761ee05b075d40a71" [[package]] name = "scc" -version = "2.3.0" +version = "2.3.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "28e1c91382686d21b5ac7959341fcb9780fa7c03773646995a87c950fa7be640" +checksum = "ea091f6cac2595aa38993f04f4ee692ed43757035c36e67c180b6828356385b1" dependencies = [ "sdd", ] @@ -6292,15 +6288,15 @@ version = "0.7.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "da046153aa2352493d6cb7da4b6e5c0c057d8a1d0a9aa8560baffdd945acd414" dependencies = [ - "ring 0.17.8", + "ring 0.17.11", "untrusted 0.9.0", ] [[package]] name = "sdd" -version = "3.0.5" +version = "3.0.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "478f121bb72bbf63c52c93011ea1791dca40140dfe13f8336c4c5ac952c33aa9" +checksum = "b07779b9b918cc05650cb30f404d4d7835d26df37c235eded8a6832e2fb82cca" [[package]] name = "seahash" @@ -6328,7 +6324,7 @@ version = "2.11.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "897b2245f0b511c87893af39b033e5ca9cce68824c4d7e7630b5a1d339658d02" dependencies = [ - "bitflags 2.8.0", + "bitflags 2.9.0", "core-foundation", "core-foundation-sys", "libc", @@ -6424,9 +6420,9 @@ dependencies = [ [[package]] name = "serde" -version = "1.0.217" +version = "1.0.218" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "02fc4265df13d6fa1d00ecff087228cc0a2b5f3c0e87e258d8b94a156e984c70" +checksum = "e8dfc9d19bdbf6d17e22319da49161d5d0108e4188e8b680aef6299eed22df60" dependencies = [ "serde_derive", ] @@ -6442,20 +6438,20 @@ dependencies = [ [[package]] name = "serde_derive" -version = "1.0.217" +version = "1.0.218" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5a9bf7cf98d04a2b28aead066b7496853d4779c9cc183c440dbac457641e19a0" +checksum = "f09503e191f4e797cb8aac08e9a4a4695c5edf6a2e70e376d961ddd5c969f82b" dependencies = [ "proc-macro2", "quote", - "syn 2.0.96", + "syn 2.0.98", ] [[package]] name = "serde_json" -version = "1.0.137" +version = "1.0.139" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "930cfb6e6abf99298aaad7d29abbef7a9999a9a8806a40088f55f0dcec03146b" +checksum = "44f86c3acccc9c65b153fe1b85a3be07fe5515274ec9f0653b4a0875731c72a6" dependencies = [ "indexmap 2.7.1", "itoa", @@ -6531,7 +6527,7 @@ checksum = "5d69265a08751de7844521fd15003ae0a888e035773ba05695c5c759a6f89eef" dependencies = [ "proc-macro2", "quote", - "syn 2.0.96", + "syn 2.0.98", ] [[package]] @@ -6651,12 +6647,6 @@ version = "0.1.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e3a9fe34e3e7a50316060351f37187a3f546bce95496156754b601a5fa71b76e" -[[package]] -name = "siphasher" -version = "0.3.11" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "38b58827f4464d87d377d175e90bf58eb00fd8716ff0a62f80356b5e61555d0d" - [[package]] name = "siphasher" version = "1.0.1" @@ -6674,9 +6664,9 @@ dependencies = [ [[package]] name = "smallvec" -version = "1.13.2" +version = "1.14.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3c5e1a9a646d36c3599cd173a41282daf47c44583ad367b8e6837255952e5c67" +checksum = "7fcf8323ef1faaee30a44a340193b1ac6814fd9b7b4e88e9d4519a3e4abe1cfd" [[package]] name = "smtp" @@ -6706,7 +6696,8 @@ dependencies = [ "rayon", "regex", "reqwest 0.12.12", - "rustls 0.23.21", + "rkyv 0.8.10", + "rustls 0.23.23", "rustls-pemfile 2.2.0", "rustls-pki-types", "serde", @@ -6718,20 +6709,20 @@ dependencies = [ "spam-filter", "store", "tokio", - "tokio-rustls 0.26.1", + "tokio-rustls 0.26.2", "trc", "utils", - "webpki-roots 0.26.7", + "webpki-roots 0.26.8", "x509-parser 0.16.0", ] [[package]] name = "smtp-proto" -version = "0.1.5" +version = "0.1.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "51b8ad3dd187f0d4debab02ad65405a9919d6a4f7bce25bd64a258781063a53a" +checksum = "b7d3950ab75b03c52f2f13fd52aab91c9d62698b231b67240e85c3ef5301e63e" dependencies = [ - "serde", + "rkyv 0.8.10", ] [[package]] @@ -6866,7 +6857,7 @@ dependencies = [ "bitpacking", "blake3", "bytes", - "deadpool 0.12.1", + "deadpool 0.12.2", "deadpool-postgres", "elasticsearch", "farmhash", @@ -6886,19 +6877,19 @@ dependencies = [ "redis", "regex", "reqwest 0.12.12", - "ring 0.17.8", + "ring 0.17.11", "rkyv 0.8.10", "roaring", "rocksdb", "rusqlite", "rust-s3", - "rustls 0.23.21", + "rustls 0.23.23", "rustls-pki-types", "serde", "serde_json", "tokio", "tokio-postgres", - "tokio-rustls 0.26.1", + "tokio-rustls 0.26.2", "trc", "utils", "xxhash-rust", @@ -6906,14 +6897,13 @@ dependencies = [ [[package]] name = "string_cache" -version = "0.8.7" +version = "0.8.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f91138e76242f575eb1d3b38b4f1362f10d3a43f47d182a5b359af488a02293b" +checksum = "938d512196766101d333398efde81bc1f37b00cb42c2f8350e5df639f040bbbe" dependencies = [ "new_debug_unreachable", - "once_cell", "parking_lot", - "phf_shared 0.10.0", + "phf_shared", "precomputed-hash", ] @@ -6969,9 +6959,9 @@ dependencies = [ [[package]] name = "syn" -version = "2.0.96" +version = "2.0.98" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d5d0adab1ae378d7f53bdebc67a39f1f151407ef230f0ce2883572f5d8985c80" +checksum = "36147f1a48ae0ec2b5b3bc5b537d267457555a10dc06f3dbc8cb11ba3006d3b1" dependencies = [ "proc-macro2", "quote", @@ -7013,7 +7003,7 @@ checksum = "c8af7666ab7b6390ab78131fb5b0fce11d6b7a6951602017c35fa82800708971" dependencies = [ "proc-macro2", "quote", - "syn 2.0.96", + "syn 2.0.98", ] [[package]] @@ -7099,9 +7089,9 @@ dependencies = [ "pop3", "rayon", "reqwest 0.12.12", - "ring 0.17.8", + "ring 0.17.11", "rkyv 0.8.10", - "rustls 0.23.21", + "rustls 0.23.23", "rustls-pemfile 2.2.0", "rustls-pki-types", "serde", @@ -7113,7 +7103,7 @@ dependencies = [ "spam-filter", "store", "tokio", - "tokio-rustls 0.26.1", + "tokio-rustls 0.26.2", "trc", "utils", ] @@ -7144,7 +7134,7 @@ checksum = "4fee6c4efc90059e10f81e6d42c60a18f76588c3d74cb83a0b242a2b6c7504c1" dependencies = [ "proc-macro2", "quote", - "syn 2.0.96", + "syn 2.0.98", ] [[package]] @@ -7155,7 +7145,7 @@ checksum = "26afc1baea8a989337eeb52b6e72a039780ce45c3edfcc9c5b9d112feeb173c2" dependencies = [ "proc-macro2", "quote", - "syn 2.0.96", + "syn 2.0.98", ] [[package]] @@ -7251,14 +7241,14 @@ checksum = "5f5ae998a069d4b5aba8ee9dad856af7d520c3699e6159b185c2acd48155d39a" dependencies = [ "proc-macro2", "quote", - "syn 2.0.96", + "syn 2.0.98", ] [[package]] name = "tokio-postgres" -version = "0.7.12" +version = "0.7.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3b5d3742945bc7d7f210693b0c58ae542c6fd47b17adbbda0885f3dcb34a6bdb" +checksum = "6c95d533c83082bb6490e0189acaa0bbeef9084e60471b696ca6988cd0541fb0" dependencies = [ "async-trait", "byteorder", @@ -7273,7 +7263,7 @@ dependencies = [ "pin-project-lite", "postgres-protocol", "postgres-types", - "rand 0.8.5", + "rand 0.9.0", "socket2", "tokio", "tokio-util", @@ -7303,11 +7293,11 @@ dependencies = [ [[package]] name = "tokio-rustls" -version = "0.26.1" +version = "0.26.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5f6d0975eaace0cf0fcadee4e4aaa5da15b5c079146f2cffb67c113be122bf37" +checksum = "8e727b36a1a0e8b74c376ac2211e40c2c8af09fb4013c60d910495810f008e9b" dependencies = [ - "rustls 0.23.21", + "rustls 0.23.23", "tokio", ] @@ -7335,19 +7325,19 @@ dependencies = [ "tokio", "tokio-rustls 0.25.0", "tungstenite 0.21.0", - "webpki-roots 0.26.7", + "webpki-roots 0.26.8", ] [[package]] name = "tokio-tungstenite" -version = "0.26.1" +version = "0.26.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "be4bf6fecd69fcdede0ec680aaf474cdab988f9de6bc73d3758f0160e3b7025a" +checksum = "7a9daff607c6d2bf6c16fd681ccb7eecc83e4e2cdc1ca067ffaadfca5de7f084" dependencies = [ "futures-util", "log", "tokio", - "tungstenite 0.26.1", + "tungstenite 0.26.2", ] [[package]] @@ -7371,9 +7361,9 @@ checksum = "0dd7358ecb8fc2f8d014bf86f6f638ce72ba252a2c3a2572f2a795f1d23efb41" [[package]] name = "toml_edit" -version = "0.22.22" +version = "0.22.24" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4ae48d6208a266e853d946088ed816055e556cc6028c5e8e2b84d9fa5dd7c7f5" +checksum = "17b4795ff5edd201c7cd6dca065ae59972ce77d1b80fa0a84d94950ece7d1474" dependencies = [ "indexmap 2.7.1", "toml_datetime", @@ -7391,7 +7381,7 @@ dependencies = [ "axum", "base64 0.22.1", "bytes", - "h2 0.4.7", + "h2 0.4.8", "http 1.2.0", "http-body 1.0.1", "http-body-util", @@ -7491,7 +7481,7 @@ checksum = "395ae124c09f9e6918a2310af6038fba074bcf474ac352496d5910dd59a2226d" dependencies = [ "proc-macro2", "quote", - "syn 2.0.96", + "syn 2.0.98", ] [[package]] @@ -7563,17 +7553,16 @@ dependencies = [ [[package]] name = "tungstenite" -version = "0.26.1" +version = "0.26.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "413083a99c579593656008130e29255e54dcaae495be556cc26888f211648c24" +checksum = "4793cb5e56680ecbb1d843515b23b6de9a75eb04b66643e256a396d43be33c13" dependencies = [ - "byteorder", "bytes", "data-encoding", "http 1.2.0", "httparse", "log", - "rand 0.8.5", + "rand 0.9.0", "sha1", "thiserror 2.0.11", "utf-8", @@ -7607,9 +7596,9 @@ checksum = "0e13db2e0ccd5e14a544e8a246ba2312cd25223f616442d7f2cb0e3db614236e" [[package]] name = "typenum" -version = "1.17.0" +version = "1.18.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "42ff0bf0c66b8238c6f3b578df37d0b7848e55df8577b3f74f92a69acceeb825" +checksum = "1dccffe3ce07af9386bfd29e80c0ab1a8205a2fc34e4bcd40364df902cfa8f3f" [[package]] name = "typewit" @@ -7640,9 +7629,9 @@ checksum = "5c1cb5db39152898a79168971543b1cb5020dff7fe43c8dc468b0885f5e29df5" [[package]] name = "unicode-ident" -version = "1.0.16" +version = "1.0.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a210d160f08b701c8721ba1c726c11662f877ea6b7094007e1ca9a1041945034" +checksum = "00e2473a93778eb0bad35909dff6a10d28e63f792f16ed15e404fca9d5eeedbe" [[package]] name = "unicode-normalization" @@ -7782,28 +7771,28 @@ dependencies = [ "rcgen 0.13.2", "regex", "reqwest 0.12.12", - "ring 0.17.8", + "ring 0.17.11", "rkyv 0.8.10", - "rustls 0.23.21", + "rustls 0.23.23", "rustls-pemfile 2.2.0", "rustls-pki-types", "serde", "serde_json", "smtp-proto", "tokio", - "tokio-rustls 0.26.1", + "tokio-rustls 0.26.2", "trc", - "webpki-roots 0.26.7", + "webpki-roots 0.26.8", "x509-parser 0.16.0", ] [[package]] name = "uuid" -version = "1.12.1" +version = "1.15.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b3758f5e68192bb96cc8f9b7e2c2cfdabb435499a28499a42f8f984092adad4b" +checksum = "e0f540e3240398cce6128b64ba83fdbdd86129c16a3aa1a3a252efd66eb3d587" dependencies = [ - "getrandom 0.2.15", + "getrandom 0.3.1", "serde", ] @@ -7899,7 +7888,7 @@ dependencies = [ "log", "proc-macro2", "quote", - "syn 2.0.96", + "syn 2.0.98", "wasm-bindgen-shared", ] @@ -7934,7 +7923,7 @@ checksum = "8ae87ea40c9f689fc23f209965b6fb8a99ad69aeeb0231408be24920604395de" dependencies = [ "proc-macro2", "quote", - "syn 2.0.96", + "syn 2.0.98", "wasm-bindgen-backend", "wasm-bindgen-shared", ] @@ -7987,7 +7976,7 @@ version = "0.22.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ed63aea5ce73d0ff405984102c42de94fc55a6b75765d621c65262469b3c9b53" dependencies = [ - "ring 0.17.8", + "ring 0.17.11", "untrusted 0.9.0", ] @@ -7999,9 +7988,9 @@ checksum = "5f20c57d8d7db6d3b86154206ae5d8fba62dd39573114de97c2cb0578251f8e1" [[package]] name = "webpki-roots" -version = "0.26.7" +version = "0.26.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5d642ff16b7e79272ae451b7322067cdc17cadf68c23264be9d94a32319efe7e" +checksum = "2210b291f7ea53617fbafcc4939f10914214ec15aace5ba62293a668f322c5c9" dependencies = [ "rustls-pki-types", ] @@ -8083,6 +8072,12 @@ dependencies = [ "windows-targets 0.52.6", ] +[[package]] +name = "windows-link" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6dccfd733ce2b1753b03b6d3c65edf020262ea35e20ccdf3e288043e6dd620e3" + [[package]] name = "windows-registry" version = "0.2.0" @@ -8263,9 +8258,9 @@ checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec" [[package]] name = "winnow" -version = "0.6.25" +version = "0.7.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ad699df48212c6cc6eb4435f35500ac6fd3b9913324f938aea302022ce19d310" +checksum = "0e7f4ea97f6f78012141bcdb6a216b2609f0979ada50b20ca5b52dde2eac2bb1" dependencies = [ "memchr", ] @@ -8286,7 +8281,7 @@ version = "0.33.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3268f3d866458b787f390cf61f4bbb563b922d091359f9608842999eaee3943c" dependencies = [ - "bitflags 2.8.0", + "bitflags 2.9.0", ] [[package]] @@ -8396,7 +8391,7 @@ checksum = "2380878cad4ac9aac1e2435f3eb4020e8374b5f13c296cb75b4620ff8e229154" dependencies = [ "proc-macro2", "quote", - "syn 2.0.96", + "syn 2.0.98", "synstructure 0.13.1", ] @@ -8412,11 +8407,11 @@ dependencies = [ [[package]] name = "zerocopy" -version = "0.8.20" +version = "0.8.21" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dde3bb8c68a8f3f1ed4ac9221aad6b10cece3e60a8e2ea54a6a2dec806d0084c" +checksum = "dcf01143b2dd5d134f11f545cf9f1431b13b749695cb33bcce051e7568f99478" dependencies = [ - "zerocopy-derive 0.8.20", + "zerocopy-derive 0.8.21", ] [[package]] @@ -8427,38 +8422,38 @@ checksum = "fa4f8080344d4671fb4e831a13ad1e68092748387dfc4f55e356242fae12ce3e" dependencies = [ "proc-macro2", "quote", - "syn 2.0.96", + "syn 2.0.98", ] [[package]] name = "zerocopy-derive" -version = "0.8.20" +version = "0.8.21" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eea57037071898bf96a6da35fd626f4f27e9cee3ead2a6c703cf09d472b2e700" +checksum = "712c8386f4f4299382c9abee219bee7084f78fb939d88b6840fcc1320d5f6da2" dependencies = [ "proc-macro2", "quote", - "syn 2.0.96", + "syn 2.0.98", ] [[package]] name = "zerofrom" -version = "0.1.5" +version = "0.1.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cff3ee08c995dee1859d998dea82f7374f2826091dd9cd47def953cae446cd2e" +checksum = "50cc42e0333e05660c3587f3bf9d0478688e15d870fab3346451ce7f8c9fbea5" dependencies = [ "zerofrom-derive", ] [[package]] name = "zerofrom-derive" -version = "0.1.5" +version = "0.1.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "595eed982f7d355beb85837f651fa22e90b3c044842dc7f2c2842c086f295808" +checksum = "d71e5d6e06ab090c67b5e44993ec16b72dcbaabc526db883a360057678b48502" dependencies = [ "proc-macro2", "quote", - "syn 2.0.96", + "syn 2.0.98", "synstructure 0.13.1", ] @@ -8479,7 +8474,7 @@ checksum = "ce36e65b0d2999d2aafac989fb249189a141aee1f53c612c1f37d72631959f69" dependencies = [ "proc-macro2", "quote", - "syn 2.0.96", + "syn 2.0.98", ] [[package]] @@ -8501,14 +8496,14 @@ checksum = "6eafa6dfb17584ea3e2bd6e76e0cc15ad7af12b09abdd1ca55961bed9b1063c6" dependencies = [ "proc-macro2", "quote", - "syn 2.0.96", + "syn 2.0.98", ] [[package]] name = "zip" -version = "2.2.2" +version = "2.2.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ae9c1ea7b3a5e1f4b922ff856a129881167511563dc219869afe3787fc0c1a45" +checksum = "b280484c454e74e5fff658bbf7df8fdbe7a07c6b2de4a53def232c15ef138f3a" dependencies = [ "aes", "arbitrary", @@ -8549,27 +8544,27 @@ dependencies = [ [[package]] name = "zstd" -version = "0.13.2" +version = "0.13.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fcf2b778a664581e31e389454a7072dab1647606d44f7feea22cd5abb9c9f3f9" +checksum = "e91ee311a569c327171651566e07972200e76fcfe2242a4fa446149a3881c08a" dependencies = [ "zstd-safe", ] [[package]] name = "zstd-safe" -version = "7.2.1" +version = "7.2.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "54a3ab4db68cea366acc5c897c7b4d4d1b8994a9cd6e6f841f8964566a419059" +checksum = "f3051792fbdc2e1e143244dc28c60f73d8470e93f3f9cbd0ead44da5ed802722" dependencies = [ "zstd-sys", ] [[package]] name = "zstd-sys" -version = "2.0.13+zstd.1.5.6" +version = "2.0.14+zstd.1.5.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "38ff0f21cfee8f97d94cef41359e0c89aa6113028ab0291aa8ca0038995a95aa" +checksum = "8fb060d4926e4ac3a3ad15d864e99ceb5f343c6b34f5bd6d81ae6ed417311be5" dependencies = [ "cc", "pkg-config", diff --git a/crates/common/Cargo.toml b/crates/common/Cargo.toml index b8d56032..f29073cd 100644 --- a/crates/common/Cargo.toml +++ b/crates/common/Cargo.toml @@ -17,7 +17,7 @@ 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"] } -smtp-proto = { version = "0.1", features = ["serde_support"] } +smtp-proto = { version = "0.1", features = ["rkyv"] } dns-update = { version = "0.1" } ahash = { version = "0.8.2", features = ["serde"] } parking_lot = "0.12.1" diff --git a/crates/common/src/config/smtp/queue.rs b/crates/common/src/config/smtp/queue.rs index e2231f79..44123a69 100644 --- a/crates/common/src/config/smtp/queue.rs +++ b/crates/common/src/config/smtp/queue.rs @@ -8,7 +8,7 @@ use ahash::AHashMap; use mail_auth::IpLookupStrategy; use mail_send::Credentials; use throttle::parse_queue_rate_limiter_key; -use utils::config::{utils::ParseValue, Config}; +use utils::config::{Config, utils::ParseValue}; use crate::{ config::server::ServerProtocol, @@ -101,8 +101,8 @@ pub struct QueueQuota { pub id: String, pub expr: Expression, pub keys: u16, - pub size: Option, - pub messages: Option, + pub size: Option, + pub messages: Option, } #[derive(Clone)] @@ -133,7 +133,11 @@ impl Default for QueueConfig { ), notify: IfBlock::new::<()>("queue.schedule.notify", [], "[1d, 3d]"), expire: IfBlock::new::<()>("queue.schedule.expire", [], "5d"), - hostname: IfBlock::new::<()>("queue.outbound.hostname", [], "config_get('server.hostname')"), + hostname: IfBlock::new::<()>( + "queue.outbound.hostname", + [], + "config_get('server.hostname')", + ), next_hop: IfBlock::new::<()>( "queue.outbound.next-hop", #[cfg(not(feature = "test_mode"))] @@ -548,11 +552,11 @@ fn parse_queue_quota_item(config: &mut Config, prefix: impl AsKey, id: &str) -> .unwrap_or_default(), keys, size: config - .property::>((prefix.as_str(), "size")) + .property::>((prefix.as_str(), "size")) .filter(|&v| v.as_ref().is_some_and(|v| *v > 0)) .unwrap_or_default(), messages: config - .property::>((prefix.as_str(), "messages")) + .property::>((prefix.as_str(), "messages")) .filter(|&v| v.as_ref().is_some_and(|v| *v > 0)) .unwrap_or_default(), }; diff --git a/crates/common/src/sharing/document.rs b/crates/common/src/sharing/document.rs index dd13df76..46495ae4 100644 --- a/crates/common/src/sharing/document.rs +++ b/crates/common/src/sharing/document.rs @@ -4,7 +4,7 @@ * SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-SEL */ -use jmap_proto::types::{acl::Acl, collection::Collection}; +use jmap_proto::types::{acl::Acl, collection::Collection, property::Property}; use store::{ValueKey, query::acl::AclQuery, roaring::RoaringBitmap, write::ValueClass}; use trc::AddContext; use utils::map::bitmap::Bitmap; @@ -12,7 +12,7 @@ use utils::map::bitmap::Bitmap; use crate::{Server, auth::AccessToken}; impl Server { - pub async fn shared_documents( + pub async fn shared_containers( &self, access_token: &AccessToken, to_account_id: u32, @@ -50,29 +50,34 @@ impl Server { Ok(document_ids) } - pub async fn shared_document_children( + pub async fn shared_items( &self, access_token: &AccessToken, to_account_id: u32, - to_collection: Collection, + to_container_collection: Collection, + to_item_collection: Collection, + property: Property, check_acls: impl Into>, ) -> trc::Result { let check_acls = check_acls.into(); - let shared_documents = self - .shared_documents(access_token, to_account_id, to_collection, check_acls) + let shared_containers = self + .shared_containers( + access_token, + to_account_id, + to_container_collection, + check_acls, + ) .await?; - if shared_documents.is_empty() { - return Ok(shared_documents); + if shared_containers.is_empty() { + return Ok(shared_containers); } - let child_collection = to_collection.child_collection().unwrap(); - let child_property = child_collection.parent_property().unwrap(); let mut shared_items = RoaringBitmap::new(); - for document_id in shared_documents { + for document_id in shared_containers { if let Some(documents_in_folder) = self .get_tag( to_account_id, - child_collection, - child_property.clone(), + to_item_collection, + property.clone(), document_id, ) .await? @@ -84,7 +89,7 @@ impl Server { Ok(shared_items) } - pub async fn owned_or_shared_documents( + pub async fn owned_or_shared_containers( &self, access_token: &AccessToken, account_id: u32, @@ -98,27 +103,35 @@ impl Server { .unwrap_or_default(); if !document_ids.is_empty() && !access_token.is_member(account_id) { document_ids &= self - .shared_documents(access_token, account_id, collection, check_acls) + .shared_containers(access_token, account_id, collection, check_acls) .await?; } Ok(document_ids) } - pub async fn owned_or_shared_document_children( + pub async fn owned_or_shared_items( &self, access_token: &AccessToken, account_id: u32, - collection: Collection, + container_collection: Collection, + item_collection: Collection, + property: Property, check_acls: impl Into>, ) -> trc::Result { - let check_acls = check_acls.into(); let mut document_ids = self - .get_document_ids(account_id, collection) + .get_document_ids(account_id, item_collection) .await? .unwrap_or_default(); if !document_ids.is_empty() && !access_token.is_member(account_id) { document_ids &= self - .shared_document_children(access_token, account_id, collection, check_acls) + .shared_items( + access_token, + account_id, + container_collection, + item_collection, + property, + check_acls, + ) .await?; } Ok(document_ids) diff --git a/crates/common/src/storage/index.rs b/crates/common/src/storage/index.rs index 3818df53..a7a09ed2 100644 --- a/crates/common/src/storage/index.rs +++ b/crates/common/src/storage/index.rs @@ -16,7 +16,7 @@ use store::{ #[derive(Debug, Clone, PartialEq, Eq)] pub enum IndexValue<'x> { - Text { field: u8, value: Cow<'x, [u8]> }, + Text { field: u8, value: Cow<'x, str> }, U32 { field: u8, value: Option }, U64 { field: u8, value: Option }, U32List { field: u8, value: &'x [u32] }, @@ -49,20 +49,6 @@ pub struct ObjectIndexBuilder { changes: Option, } -impl<'x> IndexValue<'x> { - pub fn queryable_text(field: impl Into, text: &'x str) -> Self { - let mut value = Vec::with_capacity((text.len() * 2) + 1); - value.extend_from_slice(text.as_bytes()); - value.push(0); - value.extend_from_slice(text.to_lowercase().as_bytes()); - - IndexValue::Text { - field: field.into(), - value: value.into(), - } - } -} - impl Default for ObjectIndexBuilder { fn default() -> Self { Self::new() @@ -153,7 +139,7 @@ fn build_batch( if !value.is_empty() { batch.ops.push(Operation::Index { field, - key: value.into_owned(), + key: value.into_owned().into_bytes(), set, }); } @@ -250,7 +236,7 @@ fn merge_batch( if !old_value.is_empty() { batch.ops.push(Operation::Index { field, - key: old_value.into_owned(), + key: old_value.into_owned().into_bytes(), set: false, }); } @@ -258,7 +244,7 @@ fn merge_batch( if !new_value.is_empty() { batch.ops.push(Operation::Index { field, - key: new_value.into_owned(), + key: new_value.into_owned().into_bytes(), set: true, }); } diff --git a/crates/email/Cargo.toml b/crates/email/Cargo.toml index d899afa3..c749f108 100644 --- a/crates/email/Cargo.toml +++ b/crates/email/Cargo.toml @@ -13,7 +13,7 @@ jmap_proto = { path = "../jmap-proto" } common = { path = "../common" } directory = { path = "../directory" } spam-filter = { path = "../spam-filter" } -smtp-proto = { version = "0.1", features = ["serde_support"] } +smtp-proto = { version = "0.1", features = ["rkyv"] } mail-parser = { version = "0.10", features = ["full_encoding"] } mail-builder = { version = "0.4" } sieve-rs = { version = "0.6" } diff --git a/crates/email/src/identity/mod.rs b/crates/email/src/identity/mod.rs index a4b81807..905024e5 100644 --- a/crates/email/src/identity/mod.rs +++ b/crates/email/src/identity/mod.rs @@ -4,8 +4,6 @@ * SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-SEL */ -use store::Serialize; - #[derive( rkyv::Archive, rkyv::Deserialize, rkyv::Serialize, Debug, Default, Clone, PartialEq, Eq, )] @@ -23,11 +21,3 @@ pub struct EmailAddress { pub name: Option, pub email: String, } - -impl Serialize for Identity { - fn serialize(&self) -> trc::Result> { - rkyv::to_bytes::(self) - .map(|r| r.into_vec()) - .map_err(Into::into) - } -} diff --git a/crates/email/src/mailbox/index.rs b/crates/email/src/mailbox/index.rs index 391925fc..164676c3 100644 --- a/crates/email/src/mailbox/index.rs +++ b/crates/email/src/mailbox/index.rs @@ -16,10 +16,13 @@ use super::{ArchivedUidMailbox, Mailbox, UidMailbox}; impl IndexableObject for Mailbox { fn index_values(&self) -> impl Iterator> { [ - IndexValue::queryable_text(Property::Name, &self.name), + IndexValue::Text { + field: Property::Name.into(), + value: self.name.to_lowercase().into(), + }, IndexValue::Text { field: Property::Role.into(), - value: self.role.as_str().unwrap_or_default().as_bytes().into(), + value: self.role.as_str().unwrap_or_default().into(), }, IndexValue::Tag { field: Property::Role.into(), diff --git a/crates/email/src/mailbox/manage.rs b/crates/email/src/mailbox/manage.rs index 498134e5..cd8c4ec0 100644 --- a/crates/email/src/mailbox/manage.rs +++ b/crates/email/src/mailbox/manage.rs @@ -281,7 +281,7 @@ impl MailboxFnc for Server { if pos == 0 && item.eq_ignore_ascii_case("inbox") { has_inbox = true; } else { - filter.push(Filter::eq(Property::Name, item.serialize())); + filter.push(Filter::eq(Property::Name, item.to_lowercase().into_bytes())); } } filter.push(Filter::End); diff --git a/crates/email/src/message/delivery.rs b/crates/email/src/message/delivery.rs index 28aec0ad..e91c0cd0 100644 --- a/crates/email/src/message/delivery.rs +++ b/crates/email/src/message/delivery.rs @@ -21,7 +21,7 @@ pub struct IngestMessage { pub sender_address: String, pub recipients: Vec, pub message_blob: BlobHash, - pub message_size: usize, + pub message_size: u64, pub session_id: u64, } diff --git a/crates/email/src/push/mod.rs b/crates/email/src/push/mod.rs index 06cceec4..8142e383 100644 --- a/crates/email/src/push/mod.rs +++ b/crates/email/src/push/mod.rs @@ -5,7 +5,6 @@ */ use jmap_proto::types::type_state::DataType; -use store::Serialize; use utils::map::bitmap::Bitmap; #[derive( @@ -26,11 +25,3 @@ pub struct Keys { pub p256dh: Vec, pub auth: Vec, } - -impl Serialize for PushSubscription { - fn serialize(&self) -> trc::Result> { - rkyv::to_bytes::(self) - .map(|r| r.into_vec()) - .map_err(Into::into) - } -} diff --git a/crates/email/src/sieve/index.rs b/crates/email/src/sieve/index.rs index 40e42e73..7156ae0d 100644 --- a/crates/email/src/sieve/index.rs +++ b/crates/email/src/sieve/index.rs @@ -14,7 +14,7 @@ impl IndexableObject for SieveScript { [ IndexValue::Text { field: Property::Name.into(), - value: self.name.as_bytes().into(), + value: self.name.to_lowercase().into(), }, IndexValue::U32 { field: Property::IsActive.into(), diff --git a/crates/email/src/sieve/ingest.rs b/crates/email/src/sieve/ingest.rs index 0fd08b73..2c89775a 100644 --- a/crates/email/src/sieve/ingest.rs +++ b/crates/email/src/sieve/ingest.rs @@ -4,7 +4,7 @@ * SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-SEL */ -use std::{borrow::Cow, sync::Arc}; +use std::{borrow::Cow, collections::HashSet, sync::Arc}; use crate::{ mailbox::{INBOX_ID, TRASH_ID, manage::MailboxFnc}, @@ -22,7 +22,7 @@ use mail_parser::MessageParser; use sieve::{Envelope, Event, Input, Mailbox, Recipient, Sieve}; use store::{ Deserialize, Serialize, SerializeInfallible, - ahash::AHashSet, + ahash::{AHashSet, RandomState}, query::Filter, write::{Archive, Archiver, BatchBuilder, BlobOp, LegacyBincode, assert::HashedValue, now}, }; @@ -147,13 +147,7 @@ impl SieveScriptIngest for Server { size: raw_message.len(), imap_uids: Vec::new(), }; - let mut seen_ids = if let Some(seen_ids) = active_script.seen_ids { - seen_ids - .deserialize::() - .caused_by(trc::location!())? - } else { - SeenIds::default() - }; + let mut seen_ids = active_script.seen_ids; while let Some(event) = instance.run(input) { match event { @@ -540,7 +534,7 @@ impl SieveScriptIngest for Server { .update_document(active_script.document_id) .set( Property::EmailIds, - Archiver::new(seen_ids) + Archiver::new(seen_ids.ids) .serialize() .caused_by(trc::location!())?, ); @@ -579,18 +573,29 @@ impl SieveScriptIngest for Server { .min() { let (script, script_name) = self.sieve_script_compile(account_id, document_id).await?; + + let seen_ids = if let Some(seen_ids_archive_) = self + .get_property::( + account_id, + Collection::SieveScript, + document_id, + Property::EmailIds, + ) + .await? + { + let seen_ids_archive = seen_ids_archive_ + .unarchive::>() + .caused_by(trc::location!())?; + SeenIds::from(seen_ids_archive) + } else { + SeenIds::default() + }; + Ok(Some(ActiveScript { document_id, script: Arc::new(script), script_name, - seen_ids: self - .get_property::( - account_id, - Collection::SieveScript, - document_id, - Property::EmailIds, - ) - .await?, + seen_ids, })) } else { Ok(None) @@ -701,6 +706,7 @@ impl SieveScriptIngest for Server { rkyv::deserialize(unarchived_script).caused_by(trc::location!())?; let blob_hash = std::mem::replace(&mut new_script_object.blob_hash, new_blob_hash.clone()); + let new_archive = Archiver::new(new_script_object); // Update script object let mut batch = BatchBuilder::new(); @@ -711,7 +717,7 @@ impl SieveScriptIngest for Server { .assert_value(Property::Value, &script_object) .set( Property::Value, - new_script_object.serialize().caused_by(trc::location!())?, + new_archive.serialize().caused_by(trc::location!())?, ) .clear(BlobOp::Link { hash: blob_hash }) .set( @@ -725,7 +731,7 @@ impl SieveScriptIngest for Server { .await .caused_by(trc::location!())?; - Ok((sieve.inner, new_script_object.name)) + Ok((sieve.inner, new_archive.into_inner().name)) } Err(error) => Err(trc::StoreEvent::UnexpectedError .caused_by(trc::location!()) diff --git a/crates/email/src/sieve/mod.rs b/crates/email/src/sieve/mod.rs index 6261d432..c2c60b1a 100644 --- a/crates/email/src/sieve/mod.rs +++ b/crates/email/src/sieve/mod.rs @@ -6,8 +6,9 @@ use std::{collections::HashSet, sync::Arc}; +use rkyv::collections::swiss_table::ArchivedHashSet; use sieve::Sieve; -use store::{ahash::RandomState, blake3, write::Archive}; +use store::{ahash::RandomState, blake3, write::now}; use utils::BlobHash; pub mod activate; @@ -21,7 +22,7 @@ pub struct ActiveScript { pub document_id: u32, pub script_name: String, pub script: Arc, - pub seen_ids: Option, + pub seen_ids: SeenIds, } #[derive(rkyv::Archive, rkyv::Deserialize, rkyv::Serialize, Debug, Clone)] @@ -30,9 +31,7 @@ pub struct SeenIdHash { expiry: u64, } -#[derive( - rkyv::Archive, rkyv::Deserialize, rkyv::Serialize, Default, Debug, Clone, PartialEq, Eq, -)] +#[derive(Default, Debug, Clone, PartialEq, Eq)] pub struct SeenIds { pub ids: HashSet, pub has_changes: bool, @@ -94,6 +93,35 @@ impl SieveScript { pub fn set_is_active(&mut self, is_active: bool) { self.is_active = is_active; } + + pub fn with_vacation_response(mut self, vacation_response: VacationResponse) -> Self { + self.vacation_response = Some(vacation_response); + self + } +} + +impl From<&ArchivedHashSet> for SeenIds { + fn from(archived: &ArchivedHashSet) -> Self { + let mut seen = SeenIds { + ids: HashSet::with_capacity_and_hasher(archived.len(), RandomState::new()), + has_changes: false, + }; + + let now = now(); + + for hash in archived.iter() { + if hash.expiry > now { + seen.ids.insert(SeenIdHash { + hash: hash.hash, + expiry: hash.expiry.into(), + }); + } else { + seen.has_changes = true; + } + } + + seen + } } impl SeenIdHash { diff --git a/crates/email/src/sieve/serialize.rs b/crates/email/src/sieve/serialize.rs index a1b4d75a..d9f1a64e 100644 --- a/crates/email/src/sieve/serialize.rs +++ b/crates/email/src/sieve/serialize.rs @@ -7,17 +7,9 @@ use std::collections::HashSet; use serde::ser::SerializeSeq; -use store::{Serialize, ahash::RandomState, write::now}; +use store::{ahash::RandomState, write::now}; -use super::{SeenIdHash, SeenIds, SieveScript}; - -impl Serialize for SieveScript { - fn serialize(&self) -> trc::Result> { - rkyv::to_bytes::(self) - .map(|r| r.into_vec()) - .map_err(Into::into) - } -} +use super::{SeenIdHash, SeenIds}; // SeenIds serializer impl serde::Serialize for SeenIds { diff --git a/crates/email/src/submission/index.rs b/crates/email/src/submission/index.rs index b60ea1f3..df356427 100644 --- a/crates/email/src/submission/index.rs +++ b/crates/email/src/submission/index.rs @@ -14,7 +14,7 @@ impl IndexableObject for EmailSubmission { [ IndexValue::Text { field: Property::UndoStatus.into(), - value: self.undo_status.as_index().as_bytes().into(), + value: self.undo_status.as_index().into(), }, IndexValue::U32 { field: Property::EmailId.into(), diff --git a/crates/email/src/submission/mod.rs b/crates/email/src/submission/mod.rs index 847d5c05..047916aa 100644 --- a/crates/email/src/submission/mod.rs +++ b/crates/email/src/submission/mod.rs @@ -4,7 +4,6 @@ * SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-SEL */ -use store::Serialize; use utils::map::vec_map::VecMap; pub mod index; @@ -150,11 +149,3 @@ impl ArchivedDelivered { } } } - -impl Serialize for EmailSubmission { - fn serialize(&self) -> trc::Result> { - rkyv::to_bytes::(self) - .map(|r| r.into_vec()) - .map_err(Into::into) - } -} diff --git a/crates/imap/src/core/mailbox.rs b/crates/imap/src/core/mailbox.rs index 33e72ded..d88fde57 100644 --- a/crates/imap/src/core/mailbox.rs +++ b/crates/imap/src/core/mailbox.rs @@ -137,7 +137,7 @@ impl SessionData { .caused_by(trc::location!())? } else { self.server - .shared_documents(access_token, account_id, Collection::Mailbox, Acl::Read) + .shared_containers(access_token, account_id, Collection::Mailbox, Acl::Read) .await .caused_by(trc::location!())? }; diff --git a/crates/jmap-proto/src/types/collection.rs b/crates/jmap-proto/src/types/collection.rs index 49261c08..6134d523 100644 --- a/crates/jmap-proto/src/types/collection.rs +++ b/crates/jmap-proto/src/types/collection.rs @@ -34,6 +34,16 @@ pub enum Collection { } impl Collection { + pub fn parent_collection(&self) -> Option { + match self { + Collection::Email => Some(Collection::Mailbox), + Collection::CalendarEvent => Some(Collection::Calendar), + Collection::ContactCard => Some(Collection::AddressBook), + Collection::FileNode => Some(Collection::FileNode), + _ => None, + } + } + pub fn child_collection(&self) -> Option { match self { Collection::Mailbox => Some(Collection::Email), diff --git a/crates/jmap/Cargo.toml b/crates/jmap/Cargo.toml index 25643ac5..5ed721b4 100644 --- a/crates/jmap/Cargo.toml +++ b/crates/jmap/Cargo.toml @@ -52,6 +52,7 @@ memory-stats = "1.2.0" aes-gcm = "0.10.1" aes-gcm-siv = "0.11.1" rsa = "0.9.2" +rkyv = { version = "0.8.10", features = ["little_endian"] } [features] test_mode = [] diff --git a/crates/jmap/src/api/form.rs b/crates/jmap/src/api/form.rs index eff3d6f9..6b7cd154 100644 --- a/crates/jmap/src/api/form.rs +++ b/crates/jmap/src/api/form.rs @@ -183,7 +183,7 @@ impl FormHandler for Server { // Reserve and write blob let message_blob = BlobHash::generate(message.as_bytes()); - let message_size = message.len(); + let message_size = message.len() as u64; let mut batch = BatchBuilder::new(); batch.set( BlobOp::Reserve { diff --git a/crates/jmap/src/api/http.rs b/crates/jmap/src/api/http.rs index dc7be473..68f05de3 100644 --- a/crates/jmap/src/api/http.rs +++ b/crates/jmap/src/api/http.rs @@ -7,27 +7,27 @@ use std::{borrow::Cow, net::IpAddr, sync::Arc}; use common::{ - auth::{oauth::GrantType, AccessToken}, + Inner, KV_ACME, Server, + auth::{AccessToken, oauth::GrantType}, core::BuildServer, expr::{functions::ResolveVariable, *}, ipc::StateEvent, listener::{ServerInstance, SessionData, SessionManager, SessionStream}, manager::webadmin::Resource, - Inner, Server, KV_ACME, }; use directory::Permission; use http_body_util::{BodyExt, Full}; use hyper::{ + Method, StatusCode, body::{self, Bytes}, header::{self, CONTENT_TYPE}, server::conn::http1, service::service_fn, - Method, StatusCode, }; use hyper_util::rt::TokioIo; use jmap_proto::{ error::request::{RequestError, RequestLimitError}, - request::{capability::Session, Request}, + request::{Request, capability::Session}, response::Response, types::{blob::BlobId, id::Id}, }; @@ -43,23 +43,23 @@ use crate::{ auth::{ authenticate::{Authenticator, HttpHeaders}, oauth::{ - auth::OAuthApiHandler, openid::OpenIdHandler, registration::ClientRegistrationHandler, - token::TokenHandler, FormData, + FormData, auth::OAuthApiHandler, openid::OpenIdHandler, + registration::ClientRegistrationHandler, token::TokenHandler, }, rate_limit::RateLimiter, }, - blob::{download::BlobDownload, upload::BlobUpload, DownloadResponse, UploadResponse}, + blob::{DownloadResponse, UploadResponse, download::BlobDownload, upload::BlobUpload}, websocket::upgrade::WebSocketUpgrade, }; use super::{ + HtmlResponse, HttpRequest, HttpResponse, HttpResponseBody, JmapSessionManager, JsonResponse, autoconfig::Autoconfig, event_source::EventSourceHandler, form::FormHandler, - management::{troubleshoot::TroubleshootApi, ManagementApi, ManagementApiError}, + management::{ManagementApi, ManagementApiError, troubleshoot::TroubleshootApi}, request::RequestHandler, session::SessionHandler, - HtmlResponse, HttpRequest, HttpResponse, HttpResponseBody, JmapSessionManager, JsonResponse, }; pub struct HttpSessionData { @@ -485,7 +485,7 @@ impl ParseHttp for Server { if let Some(auth) = &prometheus.auth { if req .authorization_basic() - .is_none_or( |secret| secret != auth) + .is_none_or(|secret| secret != auth) { return Err(trc::AuthEvent::Failed .into_err() @@ -757,9 +757,10 @@ async fn handle_session(inner: Arc, session: SessionDat ); } Err(err) => { - trc::error!(err - .span_id(session.session_id) - .details("Failed to check for fail2ban")); + trc::error!( + err.span_id(session.session_id) + .details("Failed to check for fail2ban") + ); } } } diff --git a/crates/jmap/src/api/management/queue.rs b/crates/jmap/src/api/management/queue.rs index e7899ae3..449f0c92 100644 --- a/crates/jmap/src/api/management/queue.rs +++ b/crates/jmap/src/api/management/queue.rs @@ -22,12 +22,15 @@ use mail_parser::DateTime; use serde::{Deserializer, Serializer}; use serde_json::json; use smtp::{ - queue::{self, ErrorDetails, HostResponse, QueueId, Status, spool::SmtpSpool}, + queue::{ + self, ArchivedMessage, ArchivedStatus, DisplayArchivedResponse, ErrorDetails, HostResponse, + QueueId, Status, spool::SmtpSpool, + }, reporting::{dmarc::DmarcReporting, tls::TlsReporting}, }; use store::{ Deserialize, IterateParams, ValueKey, - write::{LegacyBincode, QueueClass, ReportEvent, ValueClass, key::DeserializeBigEndian, now}, + write::{Archive, QueueClass, ReportEvent, ValueClass, key::DeserializeBigEndian, now}, }; use trc::AddContext; use utils::url_params::UrlParams; @@ -44,7 +47,7 @@ pub struct Message { #[serde(deserialize_with = "deserialize_datetime")] #[serde(serialize_with = "serialize_datetime")] pub created: DateTime, - pub size: usize, + pub size: u64, #[serde(skip_serializing_if = "is_zero")] #[serde(default)] pub priority: i16, @@ -199,22 +202,19 @@ impl QueueManagement for Server { // Validate the access token access_token.assert_has_permission(Permission::MessageQueueGet)?; - if let Some(message) = self - .read_message(queue_id.parse().unwrap_or_default()) - .await - .filter(|message| { - tenant_domains - .as_ref() - .is_none_or(|domains| message.has_domain(domains)) - }) + if let Some(message_) = self + .read_message_archive(queue_id.parse().unwrap_or_default()) + .await? { - Ok(JsonResponse::new(json!({ - "data": Message::from(&message), - })) - .into_http_response()) - } else { - Err(trc::ResourceEvent::NotFound.into_err()) + let message = message_.unarchive::()?; + if message.is_tenant_domain(&tenant_domains) { + return Ok(JsonResponse::new(json!({ + "data": Message::from(message), + })) + .into_http_response()); + } } + Err(trc::ResourceEvent::NotFound.into_err()) } ("messages", None, &Method::PATCH) => { // Validate the access token @@ -406,7 +406,7 @@ impl QueueManagement for Server { let mut total_completed = 0; for rcpt in &message.recipients { - if rcpt.domain_idx == domain_idx { + if rcpt.domain_idx == domain_idx as u32 { total_rcpt += 1; if matches!( rcpt.status, @@ -604,65 +604,65 @@ impl QueueManagement for Server { } } -impl From<&queue::Message> for Message { - fn from(message: &queue::Message) -> Self { +impl From<&ArchivedMessage> for Message { + fn from(message: &ArchivedMessage) -> Self { let now = now(); Message { - id: message.queue_id, - return_path: message.return_path.clone(), - created: DateTime::from_timestamp(message.created as i64), - size: message.size, - priority: message.priority, - env_id: message.env_id.clone(), + id: message.queue_id.into(), + return_path: message.return_path.to_string(), + created: DateTime::from_timestamp(u64::from(message.created) as i64), + size: message.size.into(), + priority: message.priority.into(), + env_id: message.env_id.as_ref().map(|id| id.to_string()), domains: message .domains .iter() .enumerate() .map(|(idx, domain)| Domain { - name: domain.domain.clone(), + name: domain.domain.to_string(), status: match &domain.status { - Status::Scheduled => Status::Scheduled, - Status::Completed(_) => Status::Completed(String::new()), - Status::TemporaryFailure(status) => { + ArchivedStatus::Scheduled => Status::Scheduled, + ArchivedStatus::Completed(_) => Status::Completed(String::new()), + ArchivedStatus::TemporaryFailure(status) => { Status::TemporaryFailure(status.to_string()) } - Status::PermanentFailure(status) => { + ArchivedStatus::PermanentFailure(status) => { Status::PermanentFailure(status.to_string()) } }, - retry_num: domain.retry.inner, - next_retry: Some(DateTime::from_timestamp(domain.retry.due as i64)), + retry_num: domain.retry.inner.into(), + next_retry: Some(DateTime::from_timestamp(u64::from(domain.retry.due) as i64)), next_notify: if domain.notify.due > now { - DateTime::from_timestamp(domain.notify.due as i64).into() + DateTime::from_timestamp(u64::from(domain.notify.due) as i64).into() } else { None }, recipients: message .recipients .iter() - .filter(|rcpt| rcpt.domain_idx == idx) + .filter(|rcpt| u32::from(rcpt.domain_idx) == idx as u32) .map(|rcpt| Recipient { - address: rcpt.address.clone(), + address: rcpt.address.to_string(), status: match &rcpt.status { - Status::Scheduled => Status::Scheduled, - Status::Completed(status) => { + ArchivedStatus::Scheduled => Status::Scheduled, + ArchivedStatus::Completed(status) => { Status::Completed(status.response.to_string()) } - Status::TemporaryFailure(status) => { + ArchivedStatus::TemporaryFailure(status) => { Status::TemporaryFailure(status.response.to_string()) } - Status::PermanentFailure(status) => { + ArchivedStatus::PermanentFailure(status) => { Status::PermanentFailure(status.response.to_string()) } }, - orcpt: rcpt.orcpt.clone(), + orcpt: rcpt.orcpt.as_ref().map(|orcpt| orcpt.to_string()), }) .collect(), - expires: DateTime::from_timestamp(domain.expires as i64), + expires: DateTime::from_timestamp(u64::from(domain.expires) as i64), }) .collect(), - blob_hash: URL_SAFE_NO_PAD.encode::<&[u8]>(message.blob_hash.as_ref()), + blob_hash: URL_SAFE_NO_PAD.encode::<&[u8]>(message.blob_hash.0.as_slice()), } } } @@ -714,9 +714,11 @@ async fn fetch_queued_messages( .iterate( IterateParams::new(from_key, to_key).ascending(), |key, value| { - let message = LegacyBincode::::deserialize(value) - .add_context(|ctx| ctx.ctx(trc::Key::Key, key))? - .inner; + let message_ = ::deserialize(value) + .add_context(|ctx| ctx.ctx(trc::Key::Key, key))?; + let message = message_ + .unarchive::() + .add_context(|ctx| ctx.ctx(trc::Key::Key, key))?; let matches = tenant_domains .as_ref() .is_none_or(|domains| message.has_domain(domains)) @@ -751,7 +753,7 @@ async fn fetch_queued_messages( if offset == 0 { if limit == 0 || total_returned < limit { if values { - result.values.push(Message::from(&message)); + result.values.push(Message::from(message)); } else { result.ids.push(key.deserialize_be_u64(0)?); } @@ -968,3 +970,14 @@ where fn is_zero(num: &i16) -> bool { *num == 0 } + +trait IsTenantDomain { + fn is_tenant_domain(&self, tenant_domains: &Option>) -> bool; +} +impl IsTenantDomain for ArchivedMessage { + fn is_tenant_domain(&self, tenant_domains: &Option>) -> bool { + tenant_domains + .as_ref() + .is_none_or(|domains| self.has_domain(domains)) + } +} diff --git a/crates/jmap/src/api/session.rs b/crates/jmap/src/api/session.rs index e21cd838..d44109ef 100644 --- a/crates/jmap/src/api/session.rs +++ b/crates/jmap/src/api/session.rs @@ -47,7 +47,7 @@ impl SessionHandler for Server { let is_personal = !access_token.is_member(*id); let is_readonly = is_personal && self - .shared_documents(&access_token, *id, Collection::Mailbox, Acl::AddItems) + .shared_containers(&access_token, *id, Collection::Mailbox, Acl::AddItems) .await .caused_by(trc::location!())? .is_empty(); diff --git a/crates/jmap/src/auth/oauth/auth.rs b/crates/jmap/src/auth/oauth/auth.rs index b7f2bc75..2cc6b1e0 100644 --- a/crates/jmap/src/auth/oauth/auth.rs +++ b/crates/jmap/src/auth/oauth/auth.rs @@ -21,7 +21,11 @@ use rand::{ use serde::Deserialize; use serde_json::json; use std::future::Future; -use store::{Serialize, dispatch::lookup::KeyValue, write::LegacyBincode}; +use store::{ + Serialize, + dispatch::lookup::KeyValue, + write::{Archive, Archiver}, +}; use trc::AddContext; use crate::{ @@ -107,7 +111,7 @@ impl OAuthApiHandler for Server { .collect::(); // Serialize OAuth code - let value = LegacyBincode::new(OAuthCode { + let value = Archiver::new(OAuthCode { status: OAuthStatus::Authorized, account_id: access_token.primary_id(), client_id, @@ -145,20 +149,24 @@ impl OAuthApiHandler for Server { let mut success = false; // Obtain code - if let Some(mut auth_code) = self + if let Some(auth_code_) = self .core .storage .lookup - .key_get::>(KeyValue::<()>::build_key( - KV_OAUTH, - code.as_bytes(), - )) + .key_get::(KeyValue::<()>::build_key(KV_OAUTH, code.as_bytes())) .await? { - if auth_code.inner.status == OAuthStatus::Pending { - auth_code.inner.status = OAuthStatus::Authorized; - auth_code.inner.account_id = access_token.primary_id(); - let device_code = std::mem::take(&mut auth_code.inner.params); + let oauth = auth_code_ + .unarchive::() + .caused_by(trc::location!())?; + if oauth.status == OAuthStatus::Pending { + let new_oauth_code = OAuthCode { + status: OAuthStatus::Authorized, + account_id: access_token.primary_id(), + client_id: oauth.client_id.to_string(), + nonce: oauth.nonce.as_ref().map(|s| s.to_string()), + params: Default::default(), + }; success = true; // Delete issued user code @@ -175,8 +183,10 @@ impl OAuthApiHandler for Server { .key_set( KeyValue::with_prefix( KV_OAUTH, - device_code.as_bytes(), - auth_code.serialize().caused_by(trc::location!())?, + oauth.params.as_bytes(), + Archiver::new(new_oauth_code) + .serialize() + .caused_by(trc::location!())?, ) .expires(self.core.oauth.oauth_expiry_auth_code), ) @@ -232,7 +242,7 @@ impl OAuthApiHandler for Server { } // Add OAuth status - let oauth_code = LegacyBincode::new(OAuthCode { + let oauth_code = Archiver::new(OAuthCode { status: OAuthStatus::Pending, account_id: u32::MAX, client_id, diff --git a/crates/jmap/src/auth/oauth/mod.rs b/crates/jmap/src/auth/oauth/mod.rs index 2cde060e..212b51be 100644 --- a/crates/jmap/src/auth/oauth/mod.rs +++ b/crates/jmap/src/auth/oauth/mod.rs @@ -8,14 +8,26 @@ use hyper::header::CONTENT_TYPE; use serde::{Deserialize, Serialize}; use utils::map::vec_map::VecMap; -use crate::api::{http::fetch_body, HttpRequest}; +use crate::api::{HttpRequest, http::fetch_body}; pub mod auth; pub mod openid; pub mod registration; pub mod token; -#[derive(Copy, Clone, Debug, Serialize, Deserialize, PartialEq, Eq)] +#[derive( + rkyv::Serialize, + rkyv::Deserialize, + rkyv::Archive, + Copy, + Clone, + Debug, + Serialize, + Deserialize, + PartialEq, + Eq, +)] +#[rkyv(compare(PartialEq))] pub enum OAuthStatus { Authorized, TokenIssued, @@ -35,7 +47,7 @@ pub struct OAuth { pub metadata: String, } -#[derive(Debug, Serialize, Deserialize)] +#[derive(rkyv::Serialize, rkyv::Deserialize, rkyv::Archive, Debug)] pub struct OAuthCode { pub status: OAuthStatus, pub account_id: u32, diff --git a/crates/jmap/src/auth/oauth/token.rs b/crates/jmap/src/auth/oauth/token.rs index ea573b44..a8b8e3e3 100644 --- a/crates/jmap/src/auth/oauth/token.rs +++ b/crates/jmap/src/auth/oauth/token.rs @@ -13,7 +13,7 @@ use common::{ }; use hyper::StatusCode; use std::future::Future; -use store::{dispatch::lookup::KeyValue, write::LegacyBincode}; +use store::{dispatch::lookup::KeyValue, write::Archive}; use trc::AddContext; use crate::api::{ @@ -22,8 +22,8 @@ use crate::api::{ }; use super::{ - ErrorType, FormData, MAX_POST_LEN, OAuthCode, OAuthResponse, OAuthStatus, TokenResponse, - registration::ClientRegistrationHandler, + ArchivedOAuthStatus, ErrorType, FormData, MAX_POST_LEN, OAuthCode, OAuthResponse, OAuthStatus, + TokenResponse, registration::ClientRegistrationHandler, }; pub trait TokenHandler: Sync + Send { @@ -79,14 +79,13 @@ impl TokenHandler for Server { .core .storage .lookup - .key_get::>(KeyValue::<()>::build_key( - KV_OAUTH, - code.as_bytes(), - )) + .key_get::(KeyValue::<()>::build_key(KV_OAUTH, code.as_bytes())) .await? { - Some(auth_code) => { - let oauth = auth_code.inner; + Some(auth_code_) => { + let oauth = auth_code_ + .unarchive::() + .caused_by(trc::location!())?; if client_id != oauth.client_id || redirect_uri != oauth.params { TokenResponse::error(ErrorType::InvalidClient) } else if oauth.status == OAuthStatus::Authorized { @@ -95,7 +94,7 @@ impl TokenHandler for Server { .validate_client_registration( client_id, redirect_uri.into(), - oauth.account_id, + oauth.account_id.into(), ) .await? { @@ -113,10 +112,10 @@ impl TokenHandler for Server { // Issue token self.issue_token( - oauth.account_id, + oauth.account_id.into(), &oauth.client_id, issuer, - oauth.nonce, + oauth.nonce.as_ref().map(|s| s.to_string()), true, true, ) @@ -145,24 +144,27 @@ impl TokenHandler for Server { (params.get("device_code"), params.get("client_id")) { // Obtain code - if let Some(auth_code) = self + if let Some(auth_code_) = self .core .storage .lookup - .key_get::>(KeyValue::<()>::build_key( - KV_OAUTH, - device_code.as_bytes(), - )) + .key_get::(KeyValue::<()>::build_key(KV_OAUTH, device_code.as_bytes())) .await? { - let oauth = auth_code.inner; + let oauth = auth_code_ + .unarchive::() + .caused_by(trc::location!())?; response = if oauth.client_id != client_id { TokenResponse::error(ErrorType::InvalidClient) } else { match oauth.status { - OAuthStatus::Authorized => { + ArchivedOAuthStatus::Authorized => { if let Some(error) = self - .validate_client_registration(client_id, None, oauth.account_id) + .validate_client_registration( + client_id, + None, + oauth.account_id.into(), + ) .await? { TokenResponse::error(error) @@ -179,10 +181,10 @@ impl TokenHandler for Server { // Issue token self.issue_token( - oauth.account_id, + oauth.account_id.into(), &oauth.client_id, issuer, - oauth.nonce, + oauth.nonce.as_ref().map(|s| s.to_string()), true, true, ) @@ -196,10 +198,10 @@ impl TokenHandler for Server { })? } } - OAuthStatus::Pending => { + ArchivedOAuthStatus::Pending => { TokenResponse::error(ErrorType::AuthorizationPending) } - OAuthStatus::TokenIssued => { + ArchivedOAuthStatus::TokenIssued => { TokenResponse::error(ErrorType::ExpiredToken) } } diff --git a/crates/jmap/src/blob/download.rs b/crates/jmap/src/blob/download.rs index 451b4353..5db30967 100644 --- a/crates/jmap/src/blob/download.rs +++ b/crates/jmap/src/blob/download.rs @@ -7,7 +7,7 @@ use std::ops::Range; use common::{Server, auth::AccessToken}; -use jmap_proto::types::{acl::Acl, blob::BlobId, collection::Collection}; +use jmap_proto::types::{acl::Acl, blob::BlobId, collection::Collection, property::Property}; use std::future::Future; use store::BlobClass; use trc::AddContext; @@ -60,10 +60,12 @@ impl BlobDownload for Server { } => { if Collection::from(*collection) == Collection::Email { match self - .shared_document_children( + .shared_items( access_token, *account_id, Collection::Mailbox, + Collection::Email, + Property::MailboxIds, Acl::ReadItems, ) .await @@ -131,10 +133,12 @@ impl BlobDownload for Server { if Collection::from(*collection) == Collection::Email { access_token.is_member(*account_id) || self - .shared_document_children( + .shared_items( access_token, *account_id, Collection::Mailbox, + Collection::Email, + Property::MailboxIds, Acl::ReadItems, ) .await? diff --git a/crates/jmap/src/email/copy.rs b/crates/jmap/src/email/copy.rs index 9c18dcfa..a055bb3f 100644 --- a/crates/jmap/src/email/copy.rs +++ b/crates/jmap/src/email/copy.rs @@ -73,16 +73,18 @@ impl JmapEmailCopy for Server { }; let from_message_ids = self - .owned_or_shared_document_children( + .owned_or_shared_items( access_token, from_account_id, Collection::Mailbox, + Collection::Email, + Property::MailboxIds, Acl::ReadItems, ) .await?; let mailbox_ids = self.mailbox_get_or_create(account_id).await?; let can_add_mailbox_ids = if access_token.is_shared(account_id) { - self.shared_documents(access_token, account_id, Collection::Mailbox, Acl::AddItems) + self.shared_containers(access_token, account_id, Collection::Mailbox, Acl::AddItems) .await? .into() } else { diff --git a/crates/jmap/src/email/crypto.rs b/crates/jmap/src/email/crypto.rs index 1c592f5a..4e86f61f 100644 --- a/crates/jmap/src/email/crypto.rs +++ b/crates/jmap/src/email/crypto.rs @@ -18,8 +18,8 @@ use mail_builder::encoders::base64::base64_encode_mime; use mail_parser::MessageParser; use serde_json::json; use store::{ - Serialize, - write::{Archive, Archiver, BatchBuilder, serialize::rkyv_unarchive}, + Deserialize, Serialize, + write::{Archive, Archiver, BatchBuilder}, }; use trc::AddContext; @@ -131,7 +131,10 @@ impl CryptoHandler for Server { if let Err(EncryptMessageError::Error(message)) = MessageParser::new() .parse("Subject: test\r\ntest\r\n".as_bytes()) .unwrap() - .encrypt(rkyv_unarchive::(¶ms)?) + .encrypt( + ::deserialize(params.as_slice())? + .unarchive::()?, + ) .await { return Err(manage::error(message, None::)); diff --git a/crates/jmap/src/email/get.rs b/crates/jmap/src/email/get.rs index 8b973d57..21d6ee0a 100644 --- a/crates/jmap/src/email/get.rs +++ b/crates/jmap/src/email/get.rs @@ -104,10 +104,12 @@ impl EmailGet for Server { let account_id = request.account_id.document_id(); let message_ids = self - .owned_or_shared_document_children( + .owned_or_shared_items( access_token, account_id, Collection::Mailbox, + Collection::Email, + Property::MailboxIds, Acl::ReadItems, ) .await?; diff --git a/crates/jmap/src/email/import.rs b/crates/jmap/src/email/import.rs index dc58c6a4..e54bfb4b 100644 --- a/crates/jmap/src/email/import.rs +++ b/crates/jmap/src/email/import.rs @@ -54,7 +54,7 @@ impl EmailImport for Server { let valid_mailbox_ids = self.mailbox_get_or_create(account_id).await?; let can_add_mailbox_ids = if access_token.is_shared(account_id) { - self.shared_documents(access_token, account_id, Collection::Mailbox, Acl::AddItems) + self.shared_containers(access_token, account_id, Collection::Mailbox, Acl::AddItems) .await? .into() } else { diff --git a/crates/jmap/src/email/query.rs b/crates/jmap/src/email/query.rs index bd7199be..89372413 100644 --- a/crates/jmap/src/email/query.rs +++ b/crates/jmap/src/email/query.rs @@ -284,10 +284,12 @@ impl EmailQuery for Server { let mut result_set = self.filter(account_id, Collection::Email, filters).await?; if access_token.is_shared(account_id) { result_set.apply_mask( - self.shared_document_children( + self.shared_items( access_token, account_id, Collection::Mailbox, + Collection::Email, + Property::MailboxIds, Acl::ReadItems, ) .await?, diff --git a/crates/jmap/src/email/set.rs b/crates/jmap/src/email/set.rs index cadb00ef..f5fbaf8c 100644 --- a/crates/jmap/src/email/set.rs +++ b/crates/jmap/src/email/set.rs @@ -78,33 +78,39 @@ impl EmailSet for Server { // Obtain mailboxIds let mailbox_ids = self.mailbox_get_or_create(account_id).await?; - let (can_add_mailbox_ids, can_delete_mailbox_ids, can_modify_message_ids) = if access_token - .is_shared(account_id) - { - ( - self.shared_documents(access_token, account_id, Collection::Mailbox, Acl::AddItems) + let (can_add_mailbox_ids, can_delete_mailbox_ids, can_modify_message_ids) = + if access_token.is_shared(account_id) { + ( + self.shared_containers( + access_token, + account_id, + Collection::Mailbox, + Acl::AddItems, + ) + .await? + .into(), + self.shared_containers( + access_token, + account_id, + Collection::Mailbox, + Acl::RemoveItems, + ) + .await? + .into(), + self.shared_items( + access_token, + account_id, + Collection::Mailbox, + Collection::Email, + Property::MailboxIds, + Acl::ModifyItems, + ) .await? .into(), - self.shared_documents( - access_token, - account_id, - Collection::Mailbox, - Acl::RemoveItems, ) - .await? - .into(), - self.shared_document_children( - access_token, - account_id, - Collection::Mailbox, - Acl::ModifyItems, - ) - .await? - .into(), - ) - } else { - (None, None, None) - }; + } else { + (None, None, None) + }; let will_destroy = request.unwrap_destroy(); @@ -1014,10 +1020,12 @@ impl EmailSet for Server { .await? .unwrap_or_default(); let can_destroy_message_ids = if access_token.is_shared(account_id) { - self.shared_document_children( + self.shared_items( access_token, account_id, Collection::Mailbox, + Collection::Email, + Property::MailboxIds, Acl::RemoveItems, ) .await? diff --git a/crates/jmap/src/email/snippet.rs b/crates/jmap/src/email/snippet.rs index 7c0f2bfc..324053fd 100644 --- a/crates/jmap/src/email/snippet.rs +++ b/crates/jmap/src/email/snippet.rs @@ -87,10 +87,12 @@ impl EmailSearchSnippet for Server { } let account_id = request.account_id.document_id(); let document_ids = self - .owned_or_shared_document_children( + .owned_or_shared_items( access_token, account_id, Collection::Mailbox, + Collection::Email, + Property::MailboxIds, Acl::ReadItems, ) .await?; diff --git a/crates/jmap/src/identity/get.rs b/crates/jmap/src/identity/get.rs index 7b9cc4b0..7c342209 100644 --- a/crates/jmap/src/identity/get.rs +++ b/crates/jmap/src/identity/get.rs @@ -19,7 +19,7 @@ use store::{ Serialize, rkyv::{option::ArchivedOption, vec::ArchivedVec}, roaring::RoaringBitmap, - write::{Archive, BatchBuilder}, + write::{Archive, Archiver, BatchBuilder}, }; use trc::AddContext; use utils::sanitize_email; @@ -189,11 +189,11 @@ impl IdentityGet for Server { }; batch.create_document_with_id(document_id).set( Property::Value, - Identity { + Archiver::new(Identity { name, email, ..Default::default() - } + }) .serialize() .caused_by(trc::location!())?, ); diff --git a/crates/jmap/src/identity/set.rs b/crates/jmap/src/identity/set.rs index 03e38cdd..a97293a7 100644 --- a/crates/jmap/src/identity/set.rs +++ b/crates/jmap/src/identity/set.rs @@ -18,8 +18,8 @@ use jmap_proto::{ }, }; use std::future::Future; -use store::Serialize; use store::write::{Archive, BatchBuilder, log::ChangeLogBuilder}; +use store::{Serialize, write::Archiver}; use trc::AddContext; use utils::sanitize_email; @@ -96,7 +96,9 @@ impl IdentitySet for Server { .create_document() .set( Property::Value, - identity.serialize().caused_by(trc::location!())?, + Archiver::new(identity) + .serialize() + .caused_by(trc::location!())?, ); let document_id = self .store() @@ -152,7 +154,9 @@ impl IdentitySet for Server { .update_document(document_id) .set( Property::Value, - identity.serialize().caused_by(trc::location!())?, + Archiver::new(identity) + .serialize() + .caused_by(trc::location!())?, ); self.store() .write(batch) diff --git a/crates/jmap/src/mailbox/get.rs b/crates/jmap/src/mailbox/get.rs index d810b078..0f2a1ee4 100644 --- a/crates/jmap/src/mailbox/get.rs +++ b/crates/jmap/src/mailbox/get.rs @@ -54,7 +54,7 @@ impl MailboxGet for Server { let mut mailbox_ids = self.mailbox_get_or_create(account_id).await?; if access_token.is_shared(account_id) { mailbox_ids &= self - .shared_documents(access_token, account_id, Collection::Mailbox, Acl::Read) + .shared_containers(access_token, account_id, Collection::Mailbox, Acl::Read) .await?; } let message_ids = self.get_document_ids(account_id, Collection::Email).await?; diff --git a/crates/jmap/src/mailbox/query.rs b/crates/jmap/src/mailbox/query.rs index f87b39d7..6d89e329 100644 --- a/crates/jmap/src/mailbox/query.rs +++ b/crates/jmap/src/mailbox/query.rs @@ -108,7 +108,7 @@ impl MailboxQuery for Server { .await?; if access_token.is_shared(account_id) { result_set.apply_mask( - self.shared_documents(access_token, account_id, Collection::Mailbox, Acl::Read) + self.shared_containers(access_token, account_id, Collection::Mailbox, Acl::Read) .await?, ); } diff --git a/crates/jmap/src/mailbox/set.rs b/crates/jmap/src/mailbox/set.rs index d7dc3476..0f7d099e 100644 --- a/crates/jmap/src/mailbox/set.rs +++ b/crates/jmap/src/mailbox/set.rs @@ -387,70 +387,75 @@ impl MailboxSet for Server { } // Validate depth and circular parent-child relationship - let mut mailbox_parent_id = changes.parent_id; - let current_mailbox_id = update + if update .as_ref() - .map_or(u32::MAX, |(mailbox_id, _)| *mailbox_id + 1); - let mut success = false; - for depth in 0..self.core.jmap.mailbox_max_depth { - if mailbox_parent_id == current_mailbox_id { - return Ok(Err(SetError::invalid_properties() - .with_property(Property::ParentId) - .with_description("Mailbox cannot be a parent of itself."))); - } else if mailbox_parent_id == 0 { - if depth == 0 && ctx.is_shared { - return Ok(Err(SetError::forbidden() - .with_description("You are not allowed to create root folders."))); + .is_none_or(|(_, m)| m.inner.parent_id != changes.parent_id) + { + let mut mailbox_parent_id = changes.parent_id; + let current_mailbox_id = update + .as_ref() + .map_or(u32::MAX, |(mailbox_id, _)| *mailbox_id + 1); + let mut success = false; + for depth in 0..self.core.jmap.mailbox_max_depth { + if mailbox_parent_id == current_mailbox_id { + return Ok(Err(SetError::invalid_properties() + .with_property(Property::ParentId) + .with_description("Mailbox cannot be a parent of itself."))); + } else if mailbox_parent_id == 0 { + if depth == 0 && ctx.is_shared { + return Ok(Err(SetError::forbidden() + .with_description("You are not allowed to create root folders."))); + } + success = true; + break; } - success = true; - break; - } - let parent_document_id = mailbox_parent_id - 1; + let parent_document_id = mailbox_parent_id - 1; - if let Some(mailbox_) = self - .get_property::( - ctx.account_id, - Collection::Mailbox, - parent_document_id, - Property::Value, - ) - .await? - { - let mailbox = mailbox_ - .unarchive::() - .caused_by(trc::location!())?; - if depth == 0 - && ctx.is_shared - && !mailbox - .acls - .effective_acl(ctx.access_token) - .contains_any([Acl::CreateChild, Acl::Administer].into_iter()) + if let Some(mailbox_) = self + .get_property::( + ctx.account_id, + Collection::Mailbox, + parent_document_id, + Property::Value, + ) + .await? { - return Ok(Err(SetError::forbidden().with_description( - "You are not allowed to create sub mailboxes under this mailbox.", - ))); - } + let mailbox = mailbox_ + .unarchive::() + .caused_by(trc::location!())?; + if depth == 0 + && ctx.is_shared + && !mailbox + .acls + .effective_acl(ctx.access_token) + .contains_any([Acl::CreateChild, Acl::Administer].into_iter()) + { + return Ok(Err(SetError::forbidden().with_description( + "You are not allowed to create sub mailboxes under this mailbox.", + ))); + } - mailbox_parent_id = mailbox.parent_id.into(); - } else if ctx.mailbox_ids.contains(parent_document_id) { - // Parent mailbox is probably created within the same request - success = true; - break; - } else { + mailbox_parent_id = mailbox.parent_id.into(); + } else if ctx.mailbox_ids.contains(parent_document_id) { + // Parent mailbox is probably created within the same request + success = true; + break; + } else { + return Ok(Err(SetError::invalid_properties() + .with_property(Property::ParentId) + .with_description("Mailbox parent does not exist."))); + } + } + + if !success { return Ok(Err(SetError::invalid_properties() .with_property(Property::ParentId) - .with_description("Mailbox parent does not exist."))); + .with_description( + "Mailbox parent-child relationship is too deep.", + ))); } } - if !success { - return Ok(Err(SetError::invalid_properties() - .with_property(Property::ParentId) - .with_description( - "Mailbox parent-child relationship is too deep.", - ))); - } - // Verify that the mailbox role is unique. if !matches!(changes.role, SpecialUse::None) && update @@ -506,7 +511,7 @@ impl MailboxSet for Server { ctx.account_id, Collection::Mailbox, vec![ - Filter::eq(Property::Name, changes.name.as_bytes().to_vec()), + Filter::eq(Property::Name, changes.name.to_lowercase().into_bytes()), Filter::eq(Property::ParentId, changes.parent_id.serialize()), ], ) diff --git a/crates/jmap/src/push/set.rs b/crates/jmap/src/push/set.rs index 20e5e9de..a176284d 100644 --- a/crates/jmap/src/push/set.rs +++ b/crates/jmap/src/push/set.rs @@ -24,7 +24,7 @@ use std::future::Future; use store::{ Serialize, rand::{Rng, rng}, - write::{Archive, BatchBuilder, now}, + write::{Archive, Archiver, BatchBuilder, now}, }; use trc::AddContext; use utils::map::bitmap::Bitmap; @@ -108,7 +108,9 @@ impl PushSubscriptionSet for Server { .create_document() .set( Property::Value, - push.serialize().caused_by(trc::location!())?, + Archiver::new(push) + .serialize() + .caused_by(trc::location!())?, ); let document_id = self .store() @@ -154,7 +156,7 @@ impl PushSubscriptionSet for Server { for (property, value) in object.0 { if let Err(err) = response .eval_object_references(value) - .and_then(|value| validate_push_value(&property, value, &mut push, true)) + .and_then(|value| validate_push_value(&property, value, &mut push, false)) { response.not_updated.append(id, err); continue 'update; @@ -169,7 +171,9 @@ impl PushSubscriptionSet for Server { .update_document(document_id) .set( Property::Value, - push.serialize().caused_by(trc::location!())?, + Archiver::new(push) + .serialize() + .caused_by(trc::location!())?, ); self.store() .write(batch) @@ -296,5 +300,9 @@ fn validate_push_value( } } + if is_create && push.types.is_empty() { + push.types = Bitmap::all(); + } + Ok(()) } diff --git a/crates/jmap/src/submission/get.rs b/crates/jmap/src/submission/get.rs index 0daa1bb1..f65f4d2a 100644 --- a/crates/jmap/src/submission/get.rs +++ b/crates/jmap/src/submission/get.rs @@ -18,7 +18,8 @@ use jmap_proto::{ value::{Object, Value}, }, }; -use smtp::queue::{self, spool::SmtpSpool}; +use smtp::queue::{ArchivedStatus, Message, spool::SmtpSpool}; +use smtp_proto::ArchivedResponse; use std::future::Future; use store::{rkyv::option::ArchivedOption, write::Archive}; use trc::AddContext; @@ -108,28 +109,35 @@ impl EmailSubmissionGet for Server { .collect::>(); let mut is_pending = false; if let Some(queue_id) = submission.queue_id.as_ref().map(u64::from) { - if let Some(mut queued_message) = self.read_message(queue_id).await { - for rcpt in std::mem::take(&mut queued_message.recipients) { - *delivery_status.get_mut_or_insert(rcpt.address_lcase) = DeliveryStatus { - smtp_reply: match &rcpt.status { - queue::Status::Completed(reply) => { - reply.response.to_string().replace('\n', " ") - } - queue::Status::TemporaryFailure(reply) - | queue::Status::PermanentFailure(reply) => { - reply.response.to_string().replace('\n', " ") - } - queue::Status::Scheduled => "250 2.1.5 Queued".to_string(), - }, - delivered: match &rcpt.status { - queue::Status::Scheduled | queue::Status::TemporaryFailure(_) => { - Delivered::Queued - } - queue::Status::Completed(_) => Delivered::Yes, - queue::Status::PermanentFailure(_) => Delivered::No, - }, - displayed: false, - }; + if let Some(queued_message_) = self + .read_message_archive(queue_id) + .await + .caused_by(trc::location!())? + { + let queued_message = queued_message_ + .unarchive::() + .caused_by(trc::location!())?; + for rcpt in queued_message.recipients.iter() { + *delivery_status.get_mut_or_insert(rcpt.address_lcase.to_string()) = + DeliveryStatus { + smtp_reply: match &rcpt.status { + ArchivedStatus::Completed(reply) => { + format_archived_response(&reply.response) + } + ArchivedStatus::TemporaryFailure(reply) + | ArchivedStatus::PermanentFailure(reply) => { + format_archived_response(&reply.response) + } + ArchivedStatus::Scheduled => "250 2.1.5 Queued".to_string(), + }, + delivered: match &rcpt.status { + ArchivedStatus::Scheduled + | ArchivedStatus::TemporaryFailure(_) => Delivered::Queued, + ArchivedStatus::Completed(_) => Delivered::Yes, + ArchivedStatus::PermanentFailure(_) => Delivered::No, + }, + displayed: false, + }; } is_pending = true; } @@ -218,3 +226,14 @@ fn build_address(envelope: &ArchivedAddress) -> Value { ) .into() } + +fn format_archived_response(response: &ArchivedResponse) -> String { + format!( + "Code: {}, Enhanced code: {}.{}.{}, Message: {}", + response.code, + response.esc[0], + response.esc[1], + response.esc[2], + response.message.replace('\n', " "), + ) +} diff --git a/crates/jmap/src/submission/set.rs b/crates/jmap/src/submission/set.rs index 444f8ada..c605ee1e 100644 --- a/crates/jmap/src/submission/set.rs +++ b/crates/jmap/src/submission/set.rs @@ -77,10 +77,7 @@ impl EmailSubmissionSet for Server { let mut changes = ChangeLogBuilder::new(); let mut success_email_ids = HashMap::new(); for (id, object) in request.unwrap_create() { - match self - .send_message(account_id, &response, instance, object) - .await? - { + match Box::pin(self.send_message(account_id, &response, instance, object)).await? { Ok(submission) => { // Add id mapping success_email_ids.insert( @@ -650,9 +647,9 @@ impl EmailSubmissionSet for Server { addr, DeliveryStatus { delivered: if response.is_none() { - Delivered::No - } else { Delivered::Unknown + } else { + Delivered::No }, smtp_reply: response.unwrap_or_else(|| "250 2.1.5 Queued".to_string()), displayed: false, diff --git a/crates/jmap/src/vacation/set.rs b/crates/jmap/src/vacation/set.rs index 068c6bd6..a2d7e0f3 100644 --- a/crates/jmap/src/vacation/set.rs +++ b/crates/jmap/src/vacation/set.rs @@ -8,8 +8,7 @@ use std::borrow::Cow; use common::{Server, auth::AccessToken, storage::index::ObjectIndexBuilder}; use email::sieve::{ - SieveScript, VacationResponse, activate::SieveScriptActivate, - delete::SieveScriptDelete, + SieveScript, VacationResponse, activate::SieveScriptActivate, delete::SieveScriptDelete, }; use jmap_proto::{ error::set::{SetError, SetErrorType}, @@ -126,10 +125,50 @@ impl VacationResponseSet for Server { // Process changes if let Some(changes) = changes { + // Obtain current script + let document_id = self.get_vacation_sieve_script_id(account_id).await?; + let mut was_active = false; + + let (mut sieve, prev_sieve) = if let Some(document_id) = document_id { + let prev_sieve = self + .get_property::>( + account_id, + Collection::SieveScript, + document_id, + Property::Value, + ) + .await? + .ok_or_else(|| { + trc::StoreEvent::NotFound + .into_err() + .caused_by(trc::location!()) + })? + .into_deserialized::() + .caused_by(trc::location!())?; + was_active = prev_sieve.inner.is_active; + let mut sieve = prev_sieve.inner.clone(); + if sieve.vacation_response.is_none() { + sieve.vacation_response = VacationResponse::default().into(); + } + + (sieve, Some(prev_sieve)) + } else { + ( + SieveScript { + name: "vacation".into(), + is_active: false, + blob_hash: Default::default(), + size: 0, + vacation_response: VacationResponse::default().into(), + }, + None, + ) + }; + // Parse properties - let mut vacation = VacationResponse::default(); let mut is_active = false; let mut build_script = create_id.is_some(); + let vacation = sieve.vacation_response.as_mut().unwrap(); for (property, value) in changes.0 { let value = match response.eval_object_references(value) { @@ -212,45 +251,11 @@ impl VacationResponseSet for Server { } } } + sieve.is_active = is_active; - // Obtain current script - let document_id = self.get_vacation_sieve_script_id(account_id).await?; - let mut was_active = false; - - let mut obj = if let Some(document_id) = document_id { - let prev_sieve = self - .get_property::>( - account_id, - Collection::SieveScript, - document_id, - Property::Value, - ) - .await? - .ok_or_else(|| { - trc::StoreEvent::NotFound - .into_err() - .caused_by(trc::location!()) - })?; - let prev_sieve = prev_sieve - .into_deserialized::() - .caused_by(trc::location!())?; - was_active = prev_sieve.inner.is_active; - let mut sieve = prev_sieve.inner.clone(); - sieve.vacation_response = vacation.into(); - sieve.is_active = is_active; - - ObjectIndexBuilder::new() - .with_current(prev_sieve) - .with_changes(sieve) - } else { - ObjectIndexBuilder::new().with_changes(SieveScript { - name: "vacation".into(), - is_active, - blob_hash: Default::default(), - size: 0, - vacation_response: vacation.into(), - }) - }; + let mut obj = ObjectIndexBuilder::new() + .with_current_opt(prev_sieve) + .with_changes(sieve); // Update id if let Some(document_id) = document_id { diff --git a/crates/managesieve/src/core/client.rs b/crates/managesieve/src/core/client.rs index bd78c7d9..7ca5c2c3 100644 --- a/crates/managesieve/src/core/client.rs +++ b/crates/managesieve/src/core/client.rs @@ -279,7 +279,7 @@ impl Session { .filter( account_id, Collection::SieveScript, - vec![Filter::eq(Property::Name, name.as_bytes().to_vec())], + vec![Filter::eq(Property::Name, name.to_lowercase().into_bytes())], ) .await .caused_by(trc::location!()) diff --git a/crates/smtp/Cargo.toml b/crates/smtp/Cargo.toml index e7232a5b..a0d2f67b 100644 --- a/crates/smtp/Cargo.toml +++ b/crates/smtp/Cargo.toml @@ -24,7 +24,7 @@ mail-auth = { version = "0.6" } mail-send = { version = "0.5", 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"] } +smtp-proto = { version = "0.1.6", features = ["rkyv"] } sieve-rs = { version = "0.6" } ahash = { version = "0.8" } rustls = { version = "0.23.5", default-features = false, features = ["std", "ring", "tls12"] } @@ -53,7 +53,7 @@ serde_json = "1.0" num_cpus = "1.15.0" bincode = "1.3.1" chrono = "0.4" - +rkyv = { version = "0.8.10", features = ["little_endian"] } [features] test_mode = [] diff --git a/crates/smtp/src/inbound/data.rs b/crates/smtp/src/inbound/data.rs index eb9465d3..9ce60a78 100644 --- a/crates/smtp/src/inbound/data.rs +++ b/crates/smtp/src/inbound/data.rs @@ -19,9 +19,9 @@ use common::{ scripts::ScriptModification, }; use mail_auth::{ + AuthenticatedMessage, AuthenticationResults, DkimResult, DmarcResult, ReceivedSpf, common::{headers::HeaderWriter, verify::VerifySignature}, dmarc::{self, verify::DmarcParameters}, - AuthenticatedMessage, AuthenticationResults, DkimResult, DmarcResult, ReceivedSpf, }; use mail_builder::headers::{date::Date, message_id::generate_message_id_header}; use mail_parser::MessageParser; @@ -36,7 +36,7 @@ use utils::config::Rate; use crate::{ core::{Session, SessionAddress, State}, inbound::milter::Modification, - queue::{self, quota::HasQueueQuota, Message, MessageSource, QueueEnvelope, Schedule}, + queue::{self, Message, MessageSource, QueueEnvelope, Schedule, quota::HasQueueQuota}, reporting::analysis::AnalyzeReport, scripts::ScriptResult, }; @@ -417,9 +417,11 @@ impl Session { set.write_header(&mut headers); } Err(err) => { - trc::error!(trc::Error::from(err) - .span_id(self.data.session_id) - .details("Failed to ARC seal message")); + trc::error!( + trc::Error::from(err) + .span_id(self.data.session_id) + .details("Failed to ARC seal message") + ); } } } @@ -649,16 +651,18 @@ impl Session { signature.write_header(&mut headers); } Err(err) => { - trc::error!(trc::Error::from(err) - .span_id(self.data.session_id) - .details("Failed to DKIM sign message")); + trc::error!( + trc::Error::from(err) + .span_id(self.data.session_id) + .details("Failed to DKIM sign message") + ); } } } } // Update size - message.size = raw_message.len() + headers.len(); + message.size = (raw_message.len() + headers.len()) as u64; // Verify queue quota if self.server.has_quota(&mut message).await { @@ -727,7 +731,7 @@ impl Session { if message .domains .last() - .is_none_or( |d| d.domain != rcpt.domain) + .is_none_or(|d| d.domain != rcpt.domain) { let rcpt_idx = message.domains.len(); message.domains.push(queue::Domain { @@ -822,7 +826,7 @@ impl Session { } else { rcpt.flags | RCPT_NOTIFY_DELAY | RCPT_NOTIFY_FAILURE }, - domain_idx: message.domains.len() - 1, + domain_idx: (message.domains.len() - 1) as u32, orcpt: rcpt.dsn_info, }); } diff --git a/crates/smtp/src/outbound/delivery.rs b/crates/smtp/src/outbound/delivery.rs index f681204c..3c814761 100644 --- a/crates/smtp/src/outbound/delivery.rs +++ b/crates/smtp/src/outbound/delivery.rs @@ -245,7 +245,9 @@ impl QueuedMessage { // Deliver message locally let delivery_result = message .deliver_local( - recipients.iter_mut().filter(|r| r.domain_idx == domain_idx), + recipients + .iter_mut() + .filter(|r| r.domain_idx == domain_idx as u32), &server, ) .await; @@ -1112,7 +1114,7 @@ impl QueuedMessage { smtp_client, recipients .iter_mut() - .filter(|r| r.domain_idx == domain_idx), + .filter(|r| r.domain_idx == domain_idx as u32), params, ) .await @@ -1170,7 +1172,7 @@ impl QueuedMessage { smtp_client, recipients .iter_mut() - .filter(|r| r.domain_idx == domain_idx), + .filter(|r| r.domain_idx == domain_idx as u32), params, ) .await @@ -1227,7 +1229,9 @@ impl QueuedMessage { message .deliver( smtp_client, - recipients.iter_mut().filter(|r| r.domain_idx == domain_idx), + recipients + .iter_mut() + .filter(|r| r.domain_idx == domain_idx as u32), params, ) .await @@ -1277,7 +1281,9 @@ impl QueuedMessage { message .deliver( smtp_client, - recipients.iter_mut().filter(|r| r.domain_idx == domain_idx), + recipients + .iter_mut() + .filter(|r| r.domain_idx == domain_idx as u32), params, ) .await @@ -1357,7 +1363,7 @@ impl Message { ); for rcpt in &mut self.recipients { - if rcpt.domain_idx == idx { + if rcpt.domain_idx == idx as u32 { rcpt.status = std::mem::replace(&mut rcpt.status, Status::Scheduled) .into_permanent(); } @@ -1375,7 +1381,7 @@ impl Message { ); for rcpt in &mut self.recipients { - if rcpt.domain_idx == idx { + if rcpt.domain_idx == idx as u32 { rcpt.status = std::mem::replace(&mut rcpt.status, Status::Scheduled) .into_permanent(); } diff --git a/crates/smtp/src/outbound/local.rs b/crates/smtp/src/outbound/local.rs index 880cc710..154d091a 100644 --- a/crates/smtp/src/outbound/local.rs +++ b/crates/smtp/src/outbound/local.rs @@ -122,7 +122,8 @@ impl Message { .await; // Queue Message - message.size = autogenerated.message.len() + signature.as_ref().map_or(0, |s| s.len()); + message.size = + (autogenerated.message.len() + signature.as_ref().map_or(0, |s| s.len())) as u64; if server.has_quota(&mut message).await { message .queue( diff --git a/crates/smtp/src/queue/dsn.rs b/crates/smtp/src/queue/dsn.rs index 97fb572e..5d8446c1 100644 --- a/crates/smtp/src/queue/dsn.rs +++ b/crates/smtp/src/queue/dsn.rs @@ -5,13 +5,13 @@ */ use common::Server; -use mail_builder::headers::content_type::ContentType; -use mail_builder::headers::HeaderType; -use mail_builder::mime::{make_boundary, BodyPart, MimePart}; use mail_builder::MessageBuilder; +use mail_builder::headers::HeaderType; +use mail_builder::headers::content_type::ContentType; +use mail_builder::mime::{BodyPart, MimePart, make_boundary}; use mail_parser::DateTime; use smtp_proto::{ - Response, RCPT_NOTIFY_DELAY, RCPT_NOTIFY_FAILURE, RCPT_NOTIFY_NEVER, RCPT_NOTIFY_SUCCESS, + RCPT_NOTIFY_DELAY, RCPT_NOTIFY_FAILURE, RCPT_NOTIFY_NEVER, RCPT_NOTIFY_SUCCESS, Response, }; use std::fmt::Write; use std::future::Future; @@ -23,8 +23,8 @@ use crate::reporting::SmtpReporting; use super::spool::SmtpSpool; use super::{ - Domain, Error, ErrorDetails, HostResponse, Message, MessageSource, QueueEnvelope, Recipient, - Status, RCPT_DSN_SENT, RCPT_STATUS_CHANGED, + Domain, Error, ErrorDetails, HostResponse, Message, MessageSource, QueueEnvelope, + RCPT_DSN_SENT, RCPT_STATUS_CHANGED, Recipient, Status, }; pub trait SendDsn: Sync + Send { @@ -80,7 +80,7 @@ impl SendDsn for Server { continue; } - let domain = &message.domains[rcpt.domain_idx]; + let domain = &message.domains[rcpt.domain_idx as usize]; match &rcpt.status { Status::Completed(response) => { trc::event!( @@ -173,7 +173,7 @@ impl Message { if rcpt.has_flag(RCPT_DSN_SENT | RCPT_NOTIFY_NEVER) { continue; } - let domain = &self.domains[rcpt.domain_idx]; + let domain = &self.domains[rcpt.domain_idx as usize]; match &rcpt.status { Status::Completed(response) => { rcpt.flags |= RCPT_DSN_SENT | RCPT_STATUS_CHANGED; @@ -404,10 +404,11 @@ impl Message { String::new() } Err(err) => { - trc::error!(err - .span_id(self.span_id) - .details("Failed to fetch blobId") - .caused_by(trc::location!())); + trc::error!( + err.span_id(self.span_id) + .details("Failed to fetch blobId") + .caused_by(trc::location!()) + ); String::new() } @@ -452,7 +453,7 @@ impl Message { is_double_bounce.push(dsn); } Status::Scheduled => { - let domain = &self.domains[rcpt.domain_idx]; + let domain = &self.domains[rcpt.domain_idx as usize]; if let Status::PermanentFailure(err) = &domain.status { rcpt.flags |= RCPT_DSN_SENT; let mut dsn = String::new(); diff --git a/crates/smtp/src/queue/mod.rs b/crates/smtp/src/queue/mod.rs index c1cb7c3a..cdf3022a 100644 --- a/crates/smtp/src/queue/mod.rs +++ b/crates/smtp/src/queue/mod.rs @@ -11,8 +11,7 @@ use std::{ }; use common::expr::{self, functions::ResolveVariable, *}; -use serde::{Deserialize, Serialize}; -use smtp_proto::Response; +use smtp_proto::{ArchivedResponse, Response}; use store::write::now; use utils::BlobHash; @@ -24,7 +23,7 @@ pub mod throttle; pub type QueueId = u64; -#[derive(Debug, Clone, Serialize, Deserialize)] +#[derive(Debug, Clone, rkyv::Serialize, rkyv::Deserialize, rkyv::Archive)] pub struct Schedule { pub due: u64, pub inner: T, @@ -45,7 +44,7 @@ pub enum MessageSource { Autogenerated, } -#[derive(Debug, Clone, PartialEq, Eq, serde::Serialize, serde::Deserialize)] +#[derive(rkyv::Serialize, rkyv::Deserialize, rkyv::Archive, Debug, Clone, PartialEq, Eq)] pub struct Message { pub queue_id: QueueId, pub created: u64, @@ -61,20 +60,20 @@ pub struct Message { pub env_id: Option, pub priority: i16, - pub size: usize, + pub size: u64, pub quota_keys: Vec, - #[serde(skip)] + #[rkyv(with = rkyv::with::Skip)] pub span_id: u64, } -#[derive(Debug, Clone, PartialEq, Eq, serde::Serialize, serde::Deserialize)] +#[derive(rkyv::Serialize, rkyv::Deserialize, rkyv::Archive, Debug, Clone, PartialEq, Eq)] pub enum QuotaKey { Size { key: Vec, id: u64 }, Count { key: Vec, id: u64 }, } -#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)] +#[derive(rkyv::Serialize, rkyv::Deserialize, rkyv::Archive, Debug, Clone, PartialEq, Eq)] pub struct Domain { pub domain: String, pub retry: Schedule, @@ -83,9 +82,9 @@ pub struct Domain { pub status: Status<(), Error>, } -#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)] +#[derive(rkyv::Serialize, rkyv::Deserialize, rkyv::Archive, Debug, Clone, PartialEq, Eq)] pub struct Recipient { - pub domain_idx: usize, + pub domain_idx: u32, pub address: String, pub address_lcase: String, pub status: Status, HostResponse>, @@ -96,7 +95,17 @@ pub struct Recipient { pub const RCPT_DSN_SENT: u64 = 1 << 32; pub const RCPT_STATUS_CHANGED: u64 = 2 << 32; -#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)] +#[derive( + Debug, + Clone, + PartialEq, + Eq, + rkyv::Serialize, + rkyv::Deserialize, + rkyv::Archive, + serde::Serialize, + serde::Deserialize, +)] pub enum Status { #[serde(rename = "scheduled")] Scheduled, @@ -108,13 +117,13 @@ pub enum Status { PermanentFailure(E), } -#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)] +#[derive(Debug, Clone, PartialEq, Eq, rkyv::Serialize, rkyv::Deserialize, rkyv::Archive)] pub struct HostResponse { pub hostname: T, pub response: Response, } -#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)] +#[derive(Debug, Clone, PartialEq, Eq, rkyv::Serialize, rkyv::Deserialize, rkyv::Archive)] pub enum Error { DnsError(String), UnexpectedResponse(HostResponse), @@ -127,7 +136,9 @@ pub enum Error { Io(String), } -#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, Default)] +#[derive( + Debug, Clone, PartialEq, Eq, rkyv::Serialize, rkyv::Deserialize, rkyv::Archive, Default, +)] pub struct ErrorDetails { pub entity: String, pub details: String, @@ -419,6 +430,57 @@ impl Display for Error { } } +impl Display for ArchivedError { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match self { + ArchivedError::UnexpectedResponse(response) => { + write!( + f, + "Unexpected response from '{}': {}", + response.hostname.entity, + response.response.to_string() + ) + } + ArchivedError::DnsError(err) => { + write!(f, "DNS lookup failed: {err}") + } + ArchivedError::ConnectionError(details) => { + write!( + f, + "Connection to '{}' failed: {}", + details.entity, details.details + ) + } + ArchivedError::TlsError(details) => { + write!( + f, + "TLS error from '{}': {}", + details.entity, details.details + ) + } + ArchivedError::DaneError(details) => { + write!( + f, + "DANE failed to authenticate '{}': {}", + details.entity, details.details + ) + } + ArchivedError::MtaStsError(details) => { + write!(f, "MTA-STS auth failed: {details}") + } + ArchivedError::RateLimited => { + write!(f, "Rate limited") + } + ArchivedError::ConcurrencyLimited => { + write!(f, "Too many concurrent connections to remote server") + } + ArchivedError::Io(err) => { + write!(f, "Queue error: {err}") + } + } + } +} + impl Display for Status<(), Error> { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { match self { @@ -440,3 +502,16 @@ impl Display for Status, HostResponse> { } } } + +pub trait DisplayArchivedResponse { + fn to_string(&self) -> String; +} + +impl DisplayArchivedResponse for ArchivedResponse { + fn to_string(&self) -> String { + format!( + "Code: {}, Enhanced code: {}.{}.{}, Message: {}", + self.code, self.esc[0], self.esc[1], self.esc[2], self.message, + ) + } +} diff --git a/crates/smtp/src/queue/quota.rs b/crates/smtp/src/queue/quota.rs index f701a414..d8e698f8 100644 --- a/crates/smtp/src/queue/quota.rs +++ b/crates/smtp/src/queue/quota.rs @@ -6,10 +6,10 @@ use std::future::Future; -use common::{config::smtp::queue::QueueQuota, expr::functions::ResolveVariable, Server}; +use common::{Server, config::smtp::queue::QueueQuota, expr::functions::ResolveVariable}; use store::{ - write::{BatchBuilder, QueueClass, ValueClass}, ValueKey, + write::{BatchBuilder, QueueClass, ValueClass}, }; use trc::QueueEvent; @@ -23,7 +23,7 @@ pub trait HasQueueQuota: Sync + Send { &'x self, quota: &'x QueueQuota, envelope: &impl ResolveVariable, - size: usize, + size: u64, id: u64, refs: &mut Vec, session_id: u64, @@ -89,7 +89,7 @@ impl HasQueueQuota for Server { if !self .check_quota( quota, - &QueueEnvelope::new_rcpt(message, rcpt.domain_idx, rcpt_idx), + &QueueEnvelope::new_rcpt(message, rcpt.domain_idx as usize, rcpt_idx), message.size, (rcpt_idx + 1) as u64, &mut quota_keys, @@ -118,7 +118,7 @@ impl HasQueueQuota for Server { &'x self, quota: &'x QueueQuota, envelope: &impl ResolveVariable, - size: usize, + size: u64, id: u64, refs: &mut Vec, session_id: u64, @@ -139,7 +139,7 @@ impl HasQueueQuota for Server { key.as_ref().to_vec(), )))) .await - .unwrap_or(0) as usize; + .unwrap_or(0) as u64; if used_size + size > max_size { return false; } else { @@ -159,7 +159,7 @@ impl HasQueueQuota for Server { key.as_ref().to_vec(), )))) .await - .unwrap_or(0) as usize; + .unwrap_or(0) as u64; if total_messages + 1 > max_messages { return false; } else { diff --git a/crates/smtp/src/queue/spool.rs b/crates/smtp/src/queue/spool.rs index 1bb1ba1e..da4188ad 100644 --- a/crates/smtp/src/queue/spool.rs +++ b/crates/smtp/src/queue/spool.rs @@ -11,14 +11,14 @@ use std::borrow::Cow; use std::future::Future; use std::time::{Duration, SystemTime}; use store::write::key::DeserializeBigEndian; -use store::write::{BatchBuilder, BlobOp, LegacyBincode, QueueClass, ValueClass, now}; +use store::write::{Archive, Archiver, BatchBuilder, BlobOp, QueueClass, ValueClass, now}; use store::{IterateParams, Serialize, SerializeInfallible, U64_LEN, ValueKey}; use trc::ServerEvent; use utils::BlobHash; use super::{ - Domain, Message, MessageSource, QueueEnvelope, QueueId, QueuedMessage, QuotaKey, Recipient, - Schedule, Status, + ArchivedMessage, ArchivedStatus, Domain, Message, MessageSource, QueueEnvelope, QueueId, + QueuedMessage, QuotaKey, Recipient, Schedule, Status, }; pub const LOCK_EXPIRY: u64 = 300; @@ -40,6 +40,11 @@ pub trait SmtpSpool: Sync + Send { fn unlock_event(&self, queue_id: QueueId) -> impl Future + Send; fn read_message(&self, id: QueueId) -> impl Future> + Send; + + fn read_message_archive( + &self, + id: QueueId, + ) -> impl Future>> + Send; } impl SmtpSpool for Server { @@ -149,14 +154,11 @@ impl SmtpSpool for Server { } async fn read_message(&self, id: QueueId) -> Option { - match self - .store() - .get_value::>(ValueKey::from(ValueClass::Queue( - QueueClass::Message(id), - ))) - .await - { - Ok(Some(message)) => Some(message.inner), + match self.read_message_archive(id).await.and_then(|a| match a { + Some(a) => a.deserialize::().map(Some), + None => Ok(None), + }) { + Ok(Some(message)) => Some(message), Ok(None) => None, Err(err) => { trc::error!( @@ -168,6 +170,12 @@ impl SmtpSpool for Server { } } } + + async fn read_message_archive(&self, id: QueueId) -> trc::Result> { + self.store() + .get_value::(ValueKey::from(ValueClass::Queue(QueueClass::Message(id)))) + .await + } } impl Message { @@ -192,7 +200,7 @@ impl Message { // Generate id if self.size == 0 { - self.size = message.len(); + self.size = message.len() as u64; } // Reserve and write blob @@ -298,7 +306,7 @@ impl Message { ) .set( ValueClass::Queue(QueueClass::Message(self.queue_id)), - match LegacyBincode::new(self).serialize() { + match Archiver::new(self).serialize() { Ok(data) => data, Err(err) => { trc::error!( @@ -380,7 +388,7 @@ impl Message { idx }; self.recipients.push(Recipient { - domain_idx, + domain_idx: domain_idx as u32, address: rcpt.into(), address_lcase: rcpt_lcase.into(), status: Status::Scheduled, @@ -430,7 +438,7 @@ impl Message { let span_id = self.span_id; batch.set( ValueClass::Queue(QueueClass::Message(self.queue_id)), - match LegacyBincode::new(self).serialize() { + match Archiver::new(self).serialize() { Ok(data) => data, Err(err) => { trc::error!( @@ -506,3 +514,37 @@ impl Message { .is_some_and(|(_, domain)| domains.contains(&domain.to_string())) } } + +impl ArchivedMessage { + pub fn has_domain(&self, domains: &[String]) -> bool { + self.domains + .iter() + .any(|d| domains.iter().any(|dd| dd == d.domain.as_str())) + || self + .return_path + .rsplit_once('@') + .is_some_and(|(_, domain)| domains.contains(&domain.to_string())) + } + + pub fn next_delivery_event(&self) -> u64 { + let mut next_delivery = now(); + + for (pos, domain) in self + .domains + .iter() + .filter(|d| { + matches!( + d.status, + ArchivedStatus::Scheduled | ArchivedStatus::TemporaryFailure(_) + ) + }) + .enumerate() + { + if pos == 0 || domain.retry.due < next_delivery { + next_delivery = domain.retry.due.into(); + } + } + + next_delivery + } +} diff --git a/crates/spam-filter/Cargo.toml b/crates/spam-filter/Cargo.toml index 4d0e0fa8..b5f37794 100644 --- a/crates/spam-filter/Cargo.toml +++ b/crates/spam-filter/Cargo.toml @@ -10,7 +10,7 @@ nlp = { path = "../nlp" } store = { path = "../store" } trc = { path = "../trc" } common = { path = "../common" } -smtp-proto = { version = "0.1", features = ["serde_support"] } +smtp-proto = { version = "0.1", features = ["rkyv"] } mail-parser = { version = "0.10", features = ["full_encoding"] } mail-builder = { version = "0.4" } mail-auth = { version = "0.6" } diff --git a/crates/store/src/write/serialize.rs b/crates/store/src/write/serialize.rs index c56e9e52..9910f533 100644 --- a/crates/store/src/write/serialize.rs +++ b/crates/store/src/write/serialize.rs @@ -62,6 +62,10 @@ fn aligned_lz4_deflate(archive: &[u8]) -> trc::Result { lz4_flex::block::uncompressed_size(archive) .and_then(|(uncompressed_size, archive)| { let mut bytes = AlignedVec::with_capacity(uncompressed_size); + unsafe { + // SAFETY: `new_len` is equal to `capacity` and vector is initialized by lz4_flex. + bytes.set_len(uncompressed_size); + } lz4_flex::decompress_into(archive, &mut bytes)?; Ok(Archive::Aligned(bytes)) }) diff --git a/tests/Cargo.toml b/tests/Cargo.toml index 8145a924..2e310906 100644 --- a/tests/Cargo.toml +++ b/tests/Cargo.toml @@ -5,8 +5,8 @@ edition = "2024" resolver = "2" [features] -default = ["sqlite", "postgres", "mysql", "rocks", "elastic", "s3", "redis", "azure", "foundationdb"] -#default = ["sqlite", "postgres", "mysql", "rocks", "s3", "redis"] +#default = ["sqlite", "postgres", "mysql", "rocks", "elastic", "s3", "redis", "azure", "foundationdb"] +default = ["sqlite", "postgres", "mysql", "rocks", "s3", "redis"] #default = ["rocks", "redis", "s3"] sqlite = ["store/sqlite"] foundationdb = ["store/foundation", "common/foundation"] diff --git a/tests/resources/smtp/dsn/delay.eml b/tests/resources/smtp/dsn/delay.eml index dcac1178..b0fee874 100644 --- a/tests/resources/smtp/dsn/delay.eml +++ b/tests/resources/smtp/dsn/delay.eml @@ -9,11 +9,13 @@ Content-Type: multipart/report; report-type="delivery-status"; --mime_boundary Content-Type: text/plain; charset="utf-8" -Content-Transfer-Encoding: 7bit +Content-Transfer-Encoding: quoted-printable -There was a temporary problem delivering your message to the following recipients: +There was a temporary problem delivering your message to the following recip= +ients: - (connection to 'mx.domain.org' failed: Connection timeout) + (connection to 'mx.domain.org' failed: Connection tim= +eout) --mime_boundary diff --git a/tests/resources/smtp/dsn/failure.eml b/tests/resources/smtp/dsn/failure.eml index 92372885..1ae8a9f4 100644 --- a/tests/resources/smtp/dsn/failure.eml +++ b/tests/resources/smtp/dsn/failure.eml @@ -9,11 +9,12 @@ Content-Type: multipart/report; report-type="delivery-status"; --mime_boundary Content-Type: text/plain; charset="utf-8" -Content-Transfer-Encoding: 7bit +Content-Transfer-Encoding: quoted-printable Your message could not be delivered to the following recipients: - (host 'mx.example.org' rejected command 'RCPT TO:' with code 550 (5.1.2) 'User does not exist') + (host 'mx.example.org' rejected command 'RCPT TO:' with code 550 (5.1.2) 'User does not exist') --mime_boundary diff --git a/tests/resources/smtp/dsn/mixed.eml b/tests/resources/smtp/dsn/mixed.eml index 2e305f56..8e2e83c3 100644 --- a/tests/resources/smtp/dsn/mixed.eml +++ b/tests/resources/smtp/dsn/mixed.eml @@ -9,18 +9,21 @@ Content-Type: multipart/report; report-type="delivery-status"; --mime_boundary Content-Type: text/plain; charset="utf-8" -Content-Transfer-Encoding: 7bit +Content-Transfer-Encoding: quoted-printable Your message has been partially delivered: ----- Delivery to the following addresses was successful ----- - (delivered to 'mx2.example.org' with code 250 (2.1.5) 'Message accepted for delivery') + (delivered to 'mx2.example.org' with code 250 (2.1.5) 'Me= +ssage accepted for delivery') ----- There was a temporary problem delivering to these addresses ----- - (connection to 'mx.domain.org' failed: Connection timeout) + (connection to 'mx.domain.org' failed: Connection tim= +eout) ----- Delivery to the following addresses failed ----- - (host 'mx.example.org' rejected command 'RCPT TO:' with code 550 (5.1.2) 'User does not exist') + (host 'mx.example.org' rejected command 'RCPT TO:' with code 550 (5.1.2) 'User does not exist') --mime_boundary diff --git a/tests/resources/smtp/dsn/success.eml b/tests/resources/smtp/dsn/success.eml index ffc0e3a2..285e1c78 100644 --- a/tests/resources/smtp/dsn/success.eml +++ b/tests/resources/smtp/dsn/success.eml @@ -9,11 +9,12 @@ Content-Type: multipart/report; report-type="delivery-status"; --mime_boundary Content-Type: text/plain; charset="utf-8" -Content-Transfer-Encoding: 7bit +Content-Transfer-Encoding: quoted-printable Your message has been successfully delivered to the following recipients: - (delivered to 'mx2.example.org' with code 250 (2.1.5) 'Message accepted for delivery') + (delivered to 'mx2.example.org' with code 250 (2.1.5) 'Me= +ssage accepted for delivery') --mime_boundary diff --git a/tests/src/jmap/email_submission.rs b/tests/src/jmap/email_submission.rs index 49d84e96..5d3a90e9 100644 --- a/tests/src/jmap/email_submission.rs +++ b/tests/src/jmap/email_submission.rs @@ -6,10 +6,10 @@ use ahash::AHashMap; use jmap_client::{ - core::set::{SetError, SetErrorType, SetObject}, - email_submission::{query::Filter, Address, Delivered, DeliveryStatus, Displayed, UndoStatus}, - mailbox::Role, Error, + core::set::{SetError, SetErrorType, SetObject}, + email_submission::{Address, Delivered, DeliveryStatus, Displayed, UndoStatus, query::Filter}, + mailbox::Role, }; use jmap_proto::types::id::Id; use mail_parser::DateTime; @@ -251,12 +251,14 @@ pub async fn test(params: &mut JMAPTest) { assert_eq!(message.mail_from, ""); let rcpt_to = message.rcpt_to.pop().unwrap(); - assert!([ - "", - "", - "", - ] - .contains(&rcpt_to.as_str())); + assert!( + [ + "", + "", + "", + ] + .contains(&rcpt_to.as_str()) + ); assert!( message.message.contains(&email_body), @@ -468,11 +470,13 @@ pub async fn test(params: &mut JMAPTest) { set_request.arguments().on_success_destroy_email(&create_id); request.send().await.unwrap().unwrap_method_responses(); - assert!(client - .email_get(&email_id, None::>) - .await - .unwrap() - .is_none()); + assert!( + client + .email_get(&email_id, None::>) + .await + .unwrap() + .is_none() + ); smtp_settings.lock().do_stop = true; // Destroy the created mailbox, identity and all submissions @@ -568,7 +572,7 @@ pub fn spawn_mock_smtp_server() -> (mpsc::Receiver, Arc::deserialize(value)?; - assert_eq!(key.deserialize_be_u64(0)?, value.inner.queue_id); - messages.push(value.inner); + let value = + ::deserialize(value)?.deserialize::()?; + assert_eq!(key.deserialize_be_u64(0)?, value.queue_id); + messages.push(value); Ok(true) }, ) diff --git a/tests/src/smtp/outbound/dane.rs b/tests/src/smtp/outbound/dane.rs index 51d6d39c..a35241d5 100644 --- a/tests/src/smtp/outbound/dane.rs +++ b/tests/src/smtp/outbound/dane.rs @@ -15,29 +15,29 @@ use std::{ }; use common::{ + Core, config::{ server::ServerProtocol, smtp::resolver::{DnssecResolver, Resolvers, Tlsa, TlsaEntry}, }, ipc::PolicyType, - Core, }; use mail_auth::{ + MX, MessageAuthenticator, common::parse::TxtRecordParser, hickory_resolver::{ - config::{ResolverConfig, ResolverOpts}, AsyncResolver, + config::{ResolverConfig, ResolverOpts}, }, mta_sts::{ReportUri, TlsRpt}, report::tlsrpt::ResultType, - MessageAuthenticator, MX, }; use rustls_pki_types::CertificateDer; use crate::smtp::{ + DnsCache, TestSMTP, inbound::{TestMessage, TestQueueEvent, TestReportingEvent}, session::{TestSession, VerifyResponse}, - DnsCache, TestSMTP, }; use smtp::outbound::dane::{dnssec::TlsaLookup, verify::TlsaVerify}; use smtp::queue::{Error, ErrorDetails, Status}; @@ -125,7 +125,8 @@ async fn dane_verify() { .read_lines(&local.queue_receiver) .await .assert_contains(" (DANE failed to authenticate") - .assert_contains("No TLSA records found"); + .assert_contains("No TLSA reco=") + .assert_contains("rds found"); local.queue_receiver.read_event().await.assert_done(); local.queue_receiver.assert_no_events(); @@ -177,7 +178,8 @@ async fn dane_verify() { .read_lines(&local.queue_receiver) .await .assert_contains(" (DANE failed to authenticate") - .assert_contains("No matching certificates found"); + .assert_contains("No matching ") + .assert_contains("certificates found"); local.queue_receiver.read_event().await.assert_done(); local.queue_receiver.assert_no_events(); diff --git a/tests/src/smtp/outbound/mta_sts.rs b/tests/src/smtp/outbound/mta_sts.rs index f5b00eee..05df5a18 100644 --- a/tests/src/smtp/outbound/mta_sts.rs +++ b/tests/src/smtp/outbound/mta_sts.rs @@ -14,16 +14,16 @@ use common::{ ipc::PolicyType, }; use mail_auth::{ + MX, common::parse::TxtRecordParser, mta_sts::{MtaSts, ReportUri, TlsRpt}, report::tlsrpt::ResultType, - MX, }; use crate::smtp::{ + DnsCache, TestSMTP, inbound::{TestMessage, TestQueueEvent, TestReportingEvent}, session::{TestSession, VerifyResponse}, - DnsCache, TestSMTP, }; use smtp::outbound::mta_sts::{lookup::STS_TEST_POLICY, parse::ParsePolicy}; @@ -109,7 +109,8 @@ async fn mta_sts_verify() { .read_lines(&local.queue_receiver) .await .assert_contains(" (MTA-STS failed to authenticate") - .assert_contains("Record not found"); + .assert_contains("Record not f=") + .assert_contains("ound"); local.queue_receiver.read_event().await.assert_done(); // Expect TLS failure report diff --git a/tests/src/smtp/queue/dsn.rs b/tests/src/smtp/queue/dsn.rs index b1fa8ae9..22177deb 100644 --- a/tests/src/smtp/queue/dsn.rs +++ b/tests/src/smtp/queue/dsn.rs @@ -42,7 +42,7 @@ async fn generate_dsn() { path.push("smtp"); path.push("dsn"); path.push("original.txt"); - let size = fs::metadata(&path).unwrap().len() as usize; + let size = fs::metadata(&path).unwrap().len() as u64; let dsn_original = fs::read_to_string(&path).unwrap(); let flags = RCPT_NOTIFY_FAILURE | RCPT_NOTIFY_DELAY | RCPT_NOTIFY_SUCCESS;