diff --git a/Cargo.lock b/Cargo.lock index 0f112f7a..89829906 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -17,6 +17,12 @@ version = "1.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe" +[[package]] +name = "adler2" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "512761e0bb2578dd7380c6baaa0f4ce03e84f95e960231d1dec8bf4d7d6e2627" + [[package]] name = "aead" version = "0.5.2" @@ -90,7 +96,7 @@ dependencies = [ "once_cell", "serde", "version_check", - "zerocopy 0.7.35", + "zerocopy", ] [[package]] @@ -213,9 +219,9 @@ checksum = "9d151e35f61089500b617991b791fc8bfd237ae50cd5950803758a179b41e67a" [[package]] name = "arrayvec" -version = "0.7.4" +version = "0.7.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "96d30a06541fbafbc7f82ed10c06164cfbd2c401138f6addd8404629c4b16711" +checksum = "7c02d123df017efcdfbd739ef81735b36c5ba83ec3c59c80a9d7ecc718f92e50" [[package]] name = "ascii-canvas" @@ -278,7 +284,7 @@ checksum = "965c2d33e53cb6b267e148a4cb0760bc01f4904c1cd4bb4002a085bb016d1490" dependencies = [ "proc-macro2", "quote", - "syn 2.0.72", + "syn 2.0.75", "synstructure 0.13.1", ] @@ -301,7 +307,7 @@ checksum = "7b18050c2cd6fe86c3a76584ef5e0baf286d038cda203eb6223df2cc413565f7" dependencies = [ "proc-macro2", "quote", - "syn 2.0.72", + "syn 2.0.75", ] [[package]] @@ -325,7 +331,7 @@ checksum = "3b43422f69d8ff38f95f1b2bb76517c91589a924d1559a0e935d7c8ce0274c11" dependencies = [ "proc-macro2", "quote", - "syn 2.0.72", + "syn 2.0.75", ] [[package]] @@ -347,7 +353,7 @@ checksum = "16e62a023e7c117e27523144c5d2459f4397fcc3cab0085af8e2224f643a0193" dependencies = [ "proc-macro2", "quote", - "syn 2.0.72", + "syn 2.0.75", ] [[package]] @@ -358,7 +364,7 @@ checksum = "6e0c28dcc82d7c8ead5cb13beb15405b57b8546e93215673ff8ca0349a028107" dependencies = [ "proc-macro2", "quote", - "syn 2.0.72", + "syn 2.0.75", ] [[package]] @@ -405,6 +411,33 @@ dependencies = [ "url", ] +[[package]] +name = "aws-lc-rs" +version = "1.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4ae74d9bd0a7530e8afd1770739ad34b36838829d6ad61818f9230f683f5ad77" +dependencies = [ + "aws-lc-sys", + "mirai-annotations", + "paste", + "zeroize", +] + +[[package]] +name = "aws-lc-sys" +version = "0.20.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0f0e249228c6ad2d240c2dc94b714d711629d52bad946075d8e9b2f5391f0703" +dependencies = [ + "bindgen 0.69.4", + "cc", + "cmake", + "dunce", + "fs_extra", + "libc", + "paste", +] + [[package]] name = "aws-region" version = "0.25.4" @@ -471,7 +504,7 @@ dependencies = [ "cc", "cfg-if", "libc", - "miniz_oxide", + "miniz_oxide 0.7.4", "object", "rustc-demangle", ] @@ -557,12 +590,30 @@ dependencies = [ "proc-macro2", "quote", "regex", - "rustc-hash", + "rustc-hash 1.1.0", "shlex", - "syn 2.0.72", + "syn 2.0.75", "which", ] +[[package]] +name = "bindgen" +version = "0.70.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e0127a1da21afb5adaae26910922c3f7afd3d329ba1a1b98a0884cab4907a251" +dependencies = [ + "bitflags 2.6.0", + "cexpr", + "clang-sys", + "itertools 0.13.0", + "proc-macro2", + "quote", + "regex", + "rustc-hash 1.1.0", + "shlex", + "syn 2.0.75", +] + [[package]] name = "bit-set" version = "0.5.3" @@ -632,9 +683,9 @@ dependencies = [ [[package]] name = "blake3" -version = "1.5.3" +version = "1.5.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e9ec96fe9a81b5e365f9db71fe00edc4fe4ca2cc7dcb7861f0603012a7caa210" +checksum = "d82033247fd8e890df8f740e407ad4d038debb9eb1f40533fffb32e7d17dc6f7" dependencies = [ "arrayref", "arrayvec", @@ -711,7 +762,7 @@ dependencies = [ "proc-macro-crate", "proc-macro2", "quote", - "syn 2.0.72", + "syn 2.0.75", "syn_derive", ] @@ -764,9 +815,9 @@ dependencies = [ [[package]] name = "bytemuck" -version = "1.16.3" +version = "1.17.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "102087e286b4677862ea56cf8fc58bb2cdfa8725c40ffb80fe3a008eb7f2fc83" +checksum = "6fd4c6dcc3b0aea2f5c0b4b82c2b15fe39ddbc76041a310848f4706edf76bb31" [[package]] name = "byteorder" @@ -776,9 +827,9 @@ checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b" [[package]] name = "bytes" -version = "1.7.0" +version = "1.7.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fca2be1d5c43812bae364ee3f30b3afcb7877cf59f4aeb94c66f313a41d2fac9" +checksum = "8318a53db07bb3f8dca91a600466bdb3f2eaadeedfdbcf02e1accbad9271ba50" [[package]] name = "bzip2" @@ -831,12 +882,13 @@ dependencies = [ [[package]] name = "cc" -version = "1.1.7" +version = "1.1.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "26a5c3fd7bfa1ce3897a3a3501d362b2d87b7f2583ebcb4a949ec25911025cbc" +checksum = "72db2f7947ecee9b03b510377e8bb9077afa27176fdbff55c51027e976fdcc48" dependencies = [ "jobserver", "libc", + "shlex", ] [[package]] @@ -937,9 +989,9 @@ dependencies = [ [[package]] name = "clap" -version = "4.5.13" +version = "4.5.16" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0fbb260a053428790f3de475e304ff84cdbc4face759ea7a3e64c1edd938a7fc" +checksum = "ed6719fffa43d0d87e5fd8caeab59be1554fb028cd30edc88fc4369b17971019" dependencies = [ "clap_builder", "clap_derive", @@ -947,9 +999,9 @@ dependencies = [ [[package]] name = "clap_builder" -version = "4.5.13" +version = "4.5.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "64b17d7ea74e9f833c7dbf2cbe4fb12ff26783eda4782a8975b72f895c9b4d99" +checksum = "216aec2b177652e3846684cbfe25c9964d18ec45234f0f5da5157b207ed1aab6" dependencies = [ "anstream", "anstyle", @@ -966,7 +1018,7 @@ dependencies = [ "heck 0.5.0", "proc-macro2", "quote", - "syn 2.0.72", + "syn 2.0.75", ] [[package]] @@ -988,9 +1040,9 @@ dependencies = [ [[package]] name = "cmake" -version = "0.1.50" +version = "0.1.51" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a31c789563b815f77f4250caee12365734369f942439b7defd71e18a48197130" +checksum = "fb1e43aa7fd152b1f968787f7dbcdeb306d1867ff373c69955211876c053f91a" dependencies = [ "cc", ] @@ -1017,7 +1069,7 @@ dependencies = [ [[package]] name = "common" -version = "0.9.1" +version = "0.9.2" dependencies = [ "ahash 0.8.11", "arc-swap", @@ -1052,10 +1104,10 @@ dependencies = [ "pwhash", "rcgen 0.12.1", "regex", - "reqwest 0.12.5", + "reqwest 0.12.7", "ring 0.17.8", "rustls 0.23.12", - "rustls-pemfile 2.1.2", + "rustls-pemfile 2.1.3", "rustls-pki-types", "serde", "serde_json", @@ -1144,15 +1196,15 @@ dependencies = [ [[package]] name = "core-foundation-sys" -version = "0.8.6" +version = "0.8.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "06ea2b9bc92be3c2baa9334a323ebca2d6f074ff852cd1d7b11064035cd3868f" +checksum = "773648b94d0e5d620f64f280777445740e61fe701025087ec8b57f45c791888b" [[package]] name = "cpufeatures" -version = "0.2.12" +version = "0.2.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "53fe5e26ff1b7aef8bca9c6080520cfb8d9333c7568e1829cef191a9723e5504" +checksum = "51e852e6dc9a5bed1fae92dd2375037bf2b768725bf3be87811edee3249d09ad" dependencies = [ "libc", ] @@ -1336,7 +1388,7 @@ checksum = "f46882e17999c6cc590af592290432be3bce0428cb0d5f8b6715e4dc7b383eb3" dependencies = [ "proc-macro2", "quote", - "syn 2.0.72", + "syn 2.0.75", ] [[package]] @@ -1384,7 +1436,7 @@ dependencies = [ "proc-macro2", "quote", "strsim 0.11.1", - "syn 2.0.72", + "syn 2.0.75", ] [[package]] @@ -1406,7 +1458,7 @@ checksum = "d336a2a514f6ccccaa3e09b02d41d35330c07ddf03a62165fcec10bb561c7806" dependencies = [ "darling_core 0.20.10", "quote", - "syn 2.0.72", + "syn 2.0.75", ] [[package]] @@ -1486,9 +1538,9 @@ dependencies = [ [[package]] name = "decancer" -version = "3.2.3" +version = "3.2.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0d14bdff2b0fd0e3eb4f1af30efc2cb6419c734b5cb93da37521a13313218e3f" +checksum = "58bf04658e77cbb82c3c8b64d094e3d2bc75d4e182b0425a51196e5614108270" dependencies = [ "lazy_static", "paste", @@ -1558,7 +1610,7 @@ checksum = "67e77553c4162a157adbf834ebae5b415acbecbeafc7a74b0e886657506a7611" dependencies = [ "proc-macro2", "quote", - "syn 2.0.72", + "syn 2.0.75", ] [[package]] @@ -1579,7 +1631,7 @@ dependencies = [ "darling 0.20.10", "proc-macro2", "quote", - "syn 2.0.72", + "syn 2.0.75", ] [[package]] @@ -1589,7 +1641,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "206868b8242f27cecce124c19fd88157fbd0dd334df2587f36417bafbc85097b" dependencies = [ "derive_builder_core", - "syn 2.0.72", + "syn 2.0.75", ] [[package]] @@ -1624,7 +1676,7 @@ dependencies = [ [[package]] name = "directory" -version = "0.9.1" +version = "0.9.2" dependencies = [ "ahash 0.8.11", "argon2", @@ -1687,7 +1739,7 @@ checksum = "97369cbbc041bc366949bc74d34658d6cda5621039731c6310521892a3a20ae0" dependencies = [ "proc-macro2", "quote", - "syn 2.0.72", + "syn 2.0.75", ] [[package]] @@ -1706,7 +1758,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "14b0a9ca156a0285b2768b7fcb368b231e7736d3c13eef604c5c4203057bed33" dependencies = [ "hickory-client", - "reqwest 0.12.5", + "reqwest 0.12.7", "serde", "serde_json", "serde_urlencoded", @@ -1735,6 +1787,12 @@ dependencies = [ "zeroize", ] +[[package]] +name = "dunce" +version = "1.0.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "92773504d58c093f6de2459af4af33faa518c13451eb8f2b5698ed3d36e7c813" + [[package]] name = "dyn-clone" version = "1.0.17" @@ -1912,7 +1970,7 @@ dependencies = [ "heck 0.4.1", "proc-macro2", "quote", - "syn 2.0.72", + "syn 2.0.75", ] [[package]] @@ -2005,13 +2063,13 @@ checksum = "0ce7134b9999ecaf8bcd65542e436736ef32ddca1b3e06094cb6ec5755203b80" [[package]] name = "flate2" -version = "1.0.30" +version = "1.0.32" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5f54427cfd1c7829e2a139fcefea601bf088ebca651d2bf53ebc600eac295dae" +checksum = "9c0596c1eac1f9e04ed902702e9878208b336edc9d6fddc8a48387349bab3666" dependencies = [ "crc32fast", "libz-sys", - "miniz_oxide", + "miniz_oxide 0.8.0", ] [[package]] @@ -2098,7 +2156,7 @@ checksum = "f8db6653cbc621a3810d95d55bd342be3e71181d6df21a4eb29ef986202d3f9c" dependencies = [ "proc-macro2", "quote", - "syn 2.0.72", + "syn 2.0.75", "try_map", ] @@ -2108,7 +2166,7 @@ version = "0.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ace2f49db8614b7d7e3b656a12e0059b5fbd0a4da3410b1797374bec3db269fa" dependencies = [ - "bindgen", + "bindgen 0.69.4", "libc", ] @@ -2137,7 +2195,7 @@ checksum = "b0fa992f1656e1707946bbba340ad244f0814009ef8c0118eb7b658395f19a2e" dependencies = [ "frunk_proc_macro_helpers", "quote", - "syn 2.0.72", + "syn 2.0.75", ] [[package]] @@ -2149,7 +2207,7 @@ dependencies = [ "frunk_core", "proc-macro2", "quote", - "syn 2.0.72", + "syn 2.0.75", ] [[package]] @@ -2161,9 +2219,15 @@ dependencies = [ "frunk_core", "frunk_proc_macro_helpers", "quote", - "syn 2.0.72", + "syn 2.0.75", ] +[[package]] +name = "fs_extra" +version = "1.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "42703706b716c37f96a77aea830392ad231f44c9e9a67872fa5548707e11b11c" + [[package]] name = "funty" version = "2.0.0" @@ -2226,7 +2290,7 @@ checksum = "87750cf4b7a4c0625b1529e4c543c2182106e4dedc60a2a6455e00d212c489ac" dependencies = [ "proc-macro2", "quote", - "syn 2.0.72", + "syn 2.0.75", ] [[package]] @@ -2356,7 +2420,7 @@ dependencies = [ "futures-sink", "futures-util", "http 0.2.12", - "indexmap 2.3.0", + "indexmap 2.4.0", "slab", "tokio", "tokio-util", @@ -2365,9 +2429,9 @@ dependencies = [ [[package]] name = "h2" -version = "0.4.5" +version = "0.4.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fa82e28a107a8cc405f0839610bdc9b15f1e25ec7d696aa5cf173edbcb1486ab" +checksum = "524e8ac6999421f49a846c2d4411f337e53497d8ec55d67753beffa43c5d9205" dependencies = [ "atomic-waker", "bytes", @@ -2375,7 +2439,7 @@ dependencies = [ "futures-core", "futures-sink", "http 1.1.0", - "indexmap 2.3.0", + "indexmap 2.4.0", "slab", "tokio", "tokio-util", @@ -2428,6 +2492,12 @@ version = "0.3.9" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d231dfb89cfffdbc30e7fc41579ed6066ad03abda9e567ccafae602b97ec5024" +[[package]] +name = "hermit-abi" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fbf6a919d6cf397374f7dfeeea91d974c7c0a7221d0d0f4f20d859d329e53fcc" + [[package]] name = "hex" version = "0.4.3" @@ -2674,7 +2744,7 @@ dependencies = [ "bytes", "futures-channel", "futures-util", - "h2 0.4.5", + "h2 0.4.6", "http 1.1.0", "http-body 1.0.1", "httparse", @@ -2733,9 +2803,9 @@ dependencies = [ [[package]] name = "hyper-util" -version = "0.1.6" +version = "0.1.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3ab92f4f49ee4fb4f997c784b7a2e0fa70050211e0b6a287f898c3c9785ca956" +checksum = "cde7055719c54e36e95e8719f95883f22072a48ede39db7fc17a4e1d5281e9b9" dependencies = [ "bytes", "futures-channel", @@ -2889,7 +2959,7 @@ checksum = "1ec89e9337638ecdc08744df490b221a7399bf8d164eb52a665454e60e075ad6" dependencies = [ "proc-macro2", "quote", - "syn 2.0.72", + "syn 2.0.75", ] [[package]] @@ -2947,7 +3017,7 @@ checksum = "edcd27d72f2f071c64249075f42e205ff93c9a4c5f6c6da53e79ed9f9832c285" [[package]] name = "imap" -version = "0.9.1" +version = "0.9.2" dependencies = [ "ahash 0.8.11", "common", @@ -2963,7 +3033,7 @@ dependencies = [ "parking_lot", "rand", "rustls 0.23.12", - "rustls-pemfile 2.1.2", + "rustls-pemfile 2.1.3", "store", "tokio", "tokio-rustls 0.26.0", @@ -2996,9 +3066,9 @@ dependencies = [ [[package]] name = "indexmap" -version = "2.3.0" +version = "2.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "de3fc2e30ba82dd1b3911c8de1ffc143c74a914a14e99514d7637e3099df5ea0" +checksum = "93ead53efc7ea8ed3cfb0c79fc8023fbb782a5432b52830b6518941cebe6505c" dependencies = [ "equivalent", "hashbrown 0.14.5", @@ -3054,7 +3124,7 @@ dependencies = [ "socket2", "widestring", "windows-sys 0.48.0", - "winreg 0.50.0", + "winreg", ] [[package]] @@ -3065,11 +3135,11 @@ checksum = "8f518f335dce6725a761382244631d86cf0ccb2863413590b31338feb467f9c3" [[package]] name = "is-terminal" -version = "0.4.12" +version = "0.4.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f23ff5ef2b80d608d61efee834934d862cd92461afc0560dedf493e4c033738b" +checksum = "261f68e344040fbd0edea105bef17c66edf46f984ddb1115b775ce31be948f4b" dependencies = [ - "hermit-abi", + "hermit-abi 0.4.0", "libc", "windows-sys 0.52.0", ] @@ -3159,7 +3229,7 @@ dependencies = [ [[package]] name = "jmap" -version = "0.9.1" +version = "0.9.2" dependencies = [ "aes", "aes-gcm", @@ -3195,7 +3265,7 @@ dependencies = [ "rasn", "rasn-cms", "rasn-pkix", - "reqwest 0.12.5", + "reqwest 0.12.7", "rev_lines", "rsa", "sequoia-openpgp", @@ -3263,9 +3333,9 @@ dependencies = [ [[package]] name = "js-sys" -version = "0.3.69" +version = "0.3.70" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "29c15563dc2726973df627357ce0c9ddddbea194836909d655df6a75d2cf296d" +checksum = "1868808506b929d7b0cfa8f75951347aa71bb21144b7791bae35d9bccfcfe37a" dependencies = [ "wasm-bindgen", ] @@ -3276,7 +3346,7 @@ version = "0.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4ee7893dab2e44ae5f9d0173f26ff4aa327c10b01b06a72b52dd9405b628640d" dependencies = [ - "indexmap 2.3.0", + "indexmap 2.4.0", ] [[package]] @@ -3383,9 +3453,9 @@ dependencies = [ [[package]] name = "libc" -version = "0.2.155" +version = "0.2.158" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "97b3888a4aecf77e811145cadf6eef5901f4782c53886191b2f693f24761847c" +checksum = "d8adc4bb1803a324070e64a98ae98f38934d91957a99cfb3a43dcbc01bc56439" [[package]] name = "libloading" @@ -3419,7 +3489,7 @@ version = "0.16.0+8.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ce3d60bc059831dc1c83903fb45c103f75db65c5a7bf22272764d9cc683e348c" dependencies = [ - "bindgen", + "bindgen 0.69.4", "bzip2-sys", "cc", "glob", @@ -3442,9 +3512,9 @@ dependencies = [ [[package]] name = "libz-sys" -version = "1.1.18" +version = "1.1.19" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c15da26e5af7e25c90b37a2d75cdbf940cf4a55316de9d84c679c9b8bfabf82e" +checksum = "fdc53a7799a7496ebc9fd29f31f7df80e83c9bda5299768af5f9e59eeea74647" dependencies = [ "cc", "pkg-config", @@ -3552,7 +3622,7 @@ dependencies = [ "rand", "ring 0.17.8", "rsa", - "rustls-pemfile 2.1.2", + "rustls-pemfile 2.1.3", "serde", "serde_json", "zip", @@ -3596,7 +3666,7 @@ dependencies = [ [[package]] name = "mail-server" -version = "0.9.1" +version = "0.9.2" dependencies = [ "common", "directory", @@ -3615,7 +3685,7 @@ dependencies = [ [[package]] name = "managesieve" -version = "0.9.1" +version = "0.9.2" dependencies = [ "ahash 0.8.11", "bincode", @@ -3630,7 +3700,7 @@ dependencies = [ "md5", "parking_lot", "rustls 0.23.12", - "rustls-pemfile 2.1.2", + "rustls-pemfile 2.1.3", "sieve-rs", "store", "tokio", @@ -3665,7 +3735,7 @@ checksum = "5cf92c10c7e361d6b99666ec1c6f9805b0bea2c3bd8c78dc6fe98ac5bd78db11" dependencies = [ "proc-macro2", "quote", - "syn 2.0.72", + "syn 2.0.75", ] [[package]] @@ -3748,29 +3818,32 @@ dependencies = [ ] [[package]] -name = "mio" -version = "0.8.11" +name = "miniz_oxide" +version = "0.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a4a650543ca06a924e8b371db273b2756685faae30f8487da1b56505a8f78b0c" +checksum = "e2d80299ef12ff69b16a84bb182e3b9df68b5a91574d3d4fa6e41b65deec4df1" dependencies = [ - "libc", - "log", - "wasi", - "windows-sys 0.48.0", + "adler2", ] [[package]] name = "mio" -version = "1.0.1" +version = "1.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4569e456d394deccd22ce1c1913e6ea0e54519f577285001215d33557431afe4" +checksum = "80e04d1dcff3aae0704555fe5fee3bcfaf3d1fdf8a7e521d5b9d2b42acb52cec" dependencies = [ - "hermit-abi", + "hermit-abi 0.3.9", "libc", "wasi", "windows-sys 0.52.0", ] +[[package]] +name = "mirai-annotations" +version = "1.12.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c9be0862c1b3f26a88803c4a49de6889c10e608b3ee9344e6ef5b45fb37ad3d1" + [[package]] name = "mysql-common-derive" version = "0.31.1" @@ -3784,16 +3857,16 @@ dependencies = [ "proc-macro-error", "proc-macro2", "quote", - "syn 2.0.72", + "syn 2.0.75", "termcolor", "thiserror", ] [[package]] name = "mysql_async" -version = "0.34.1" +version = "0.34.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fbfe87d7e35cb72363326216cc1712b865d8d4f70abf3b2d2e6b251fb6b2f427" +checksum = "a0b66e411c31265e879d9814d03721f2daa7ad07337b6308cb4bb0cde7e6fd47" dependencies = [ "bytes", "crossbeam", @@ -3802,23 +3875,20 @@ dependencies = [ "futures-sink", "futures-util", "keyed_priority_queue", - "lazy_static", "lru", - "mio 0.8.11", "mysql_common", - "once_cell", "pem", "percent-encoding", "pin-project", "rand", - "rustls 0.22.4", - "rustls-pemfile 2.1.2", + "rustls 0.23.12", + "rustls-pemfile 2.1.3", "serde", "serde_json", "socket2", "thiserror", "tokio", - "tokio-rustls 0.25.0", + "tokio-rustls 0.26.0", "tokio-util", "twox-hash", "url", @@ -3834,7 +3904,7 @@ checksum = "478b0ff3f7d67b79da2b96f56f334431aef65e15ba4b29dd74a4236e29582bdc" dependencies = [ "base64 0.21.7", "bigdecimal", - "bindgen", + "bindgen 0.70.0", "bitflags 2.6.0", "bitvec", "btoi", @@ -3895,7 +3965,7 @@ dependencies = [ [[package]] name = "nlp" -version = "0.9.1" +version = "0.9.2" dependencies = [ "ahash 0.8.11", "bincode", @@ -4000,7 +4070,7 @@ version = "1.16.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4161fcb6d602d4d2081af7c3a45852d875a03dd337a6bfdd6e06407b61342a43" dependencies = [ - "hermit-abi", + "hermit-abi 0.3.9", "libc", ] @@ -4012,9 +4082,9 @@ checksum = "830b246a0e5f20af87141b25c173cd1b609bd7779a4617d6ec582abaf90870f3" [[package]] name = "object" -version = "0.36.2" +version = "0.36.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3f203fa8daa7bb185f760ae12bd8e097f63d17041dcdcaf675ac54cdf863170e" +checksum = "27b64972346851a39438c60b341ebc01bba47464ae329e55cf343eb93964efd9" dependencies = [ "memchr", ] @@ -4072,7 +4142,7 @@ checksum = "a948666b637a0f465e8564c73e89d4dde00d72d4d473cc972f390fc3dcee7d9c" dependencies = [ "proc-macro2", "quote", - "syn 2.0.72", + "syn 2.0.75", ] [[package]] @@ -4117,7 +4187,7 @@ dependencies = [ "bytes", "http 1.1.0", "opentelemetry", - "reqwest 0.12.5", + "reqwest 0.12.7", ] [[package]] @@ -4134,7 +4204,7 @@ dependencies = [ "opentelemetry-proto", "opentelemetry_sdk", "prost", - "reqwest 0.12.5", + "reqwest 0.12.7", "thiserror", "tokio", "tonic", @@ -4309,7 +4379,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b4c5cc86750666a3ed20bdaf5ca2a0344f9c67674cae0515bec2da16fbaa47db" dependencies = [ "fixedbitset", - "indexmap 2.3.0", + "indexmap 2.4.0", ] [[package]] @@ -4352,7 +4422,7 @@ dependencies = [ "phf_shared 0.11.2", "proc-macro2", "quote", - "syn 2.0.72", + "syn 2.0.75", ] [[package]] @@ -4390,7 +4460,7 @@ checksum = "2f38a4412a78282e09a2cf38d195ea5420d15ba0602cb375210efbc877243965" dependencies = [ "proc-macro2", "quote", - "syn 2.0.72", + "syn 2.0.75", ] [[package]] @@ -4446,7 +4516,7 @@ dependencies = [ [[package]] name = "pop3" -version = "0.9.1" +version = "0.9.2" dependencies = [ "common", "imap", @@ -4505,11 +4575,11 @@ checksum = "439ee305def115ba05938db6eb1644ff94165c5ab5e9420d1c1bcedbba909391" [[package]] name = "ppv-lite86" -version = "0.2.18" +version = "0.2.20" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dee4364d9f3b902ef14fab8a1ddffb783a1cb6b4bba3bfc1fa3922732c7de97f" +checksum = "77957b295656769bb8ad2b6a6b09d897d94f05c41b069aede1fcdaa675eaea04" dependencies = [ - "zerocopy 0.6.6", + "zerocopy", ] [[package]] @@ -4525,7 +4595,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5f12335488a2f3b0a83b14edad48dca9879ce89b2edd10e80237e4e852dd645e" dependencies = [ "proc-macro2", - "syn 2.0.72", + "syn 2.0.75", ] [[package]] @@ -4637,7 +4707,7 @@ dependencies = [ "itertools 0.13.0", "proc-macro2", "quote", - "syn 2.0.72", + "syn 2.0.75", ] [[package]] @@ -4712,16 +4782,17 @@ dependencies = [ [[package]] name = "quinn" -version = "0.11.2" +version = "0.11.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e4ceeeeabace7857413798eb1ffa1e9c905a9946a57d81fb69b4b71c4d8eb3ad" +checksum = "b22d8e7369034b9a7132bc2008cac12f2013c8132b45e0554e6e20e2617f2156" dependencies = [ "bytes", "pin-project-lite", "quinn-proto", "quinn-udp", - "rustc-hash", + "rustc-hash 2.0.0", "rustls 0.23.12", + "socket2", "thiserror", "tokio", "tracing", @@ -4729,14 +4800,14 @@ dependencies = [ [[package]] name = "quinn-proto" -version = "0.11.3" +version = "0.11.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ddf517c03a109db8100448a4be38d498df8a210a99fe0e1b9eaf39e78c640efe" +checksum = "ba92fb39ec7ad06ca2582c0ca834dfeadcaf06ddfc8e635c80aa7e1c05315fdd" dependencies = [ "bytes", "rand", "ring 0.17.8", - "rustc-hash", + "rustc-hash 2.0.0", "rustls 0.23.12", "slab", "thiserror", @@ -4753,6 +4824,7 @@ dependencies = [ "libc", "once_cell", "socket2", + "tracing", "windows-sys 0.52.0", ] @@ -4943,8 +5015,8 @@ dependencies = [ "pin-project-lite", "rand", "rustls 0.23.12", - "rustls-native-certs 0.7.1", - "rustls-pemfile 2.1.2", + "rustls-native-certs 0.7.2", + "rustls-pemfile 2.1.3", "rustls-pki-types", "ryu", "sha1_smol", @@ -4976,9 +5048,9 @@ dependencies = [ [[package]] name = "redox_users" -version = "0.4.5" +version = "0.4.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bd283d9651eeda4b2a83a43c1c91b266c40fd76ecd39a50a8c630ae69dc72891" +checksum = "ba009ff324d1fc1b900bd1fdb31564febe58a8ccc8a6fdbb93b543d33b13ca43" dependencies = [ "getrandom", "libredox", @@ -4987,9 +5059,9 @@ dependencies = [ [[package]] name = "regex" -version = "1.10.5" +version = "1.10.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b91213439dad192326a0d7c6ee3955910425f441d7038e0d6933b0aec5c4517f" +checksum = "4219d74c6b67a3654a9fbebc4b419e22126d13d2f3c4a07ee0cb61ff79a79619" dependencies = [ "aho-corasick", "memchr", @@ -5064,21 +5136,21 @@ dependencies = [ "wasm-streams", "web-sys", "webpki-roots 0.25.4", - "winreg 0.50.0", + "winreg", ] [[package]] name = "reqwest" -version = "0.12.5" +version = "0.12.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c7d6d2a27d57148378eb5e111173f4276ad26340ecc5c49a4a2152167a2d6a37" +checksum = "f8f4955649ef5c38cc7f9e8aa41761d48fb9677197daea9984dc54f56aad5e63" dependencies = [ "base64 0.22.1", "bytes", "futures-channel", "futures-core", "futures-util", - "h2 0.4.5", + "h2 0.4.6", "http 1.1.0", "http-body 1.0.1", "http-body-util", @@ -5095,7 +5167,7 @@ dependencies = [ "pin-project-lite", "quinn", "rustls 0.23.12", - "rustls-pemfile 2.1.2", + "rustls-pemfile 2.1.3", "rustls-pki-types", "serde", "serde_json", @@ -5109,7 +5181,7 @@ dependencies = [ "wasm-bindgen-futures", "web-sys", "webpki-roots 0.26.3", - "winreg 0.52.0", + "windows-registry", ] [[package]] @@ -5182,9 +5254,9 @@ dependencies = [ [[package]] name = "rkyv" -version = "0.7.44" +version = "0.7.45" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5cba464629b3394fc4dbc6f940ff8f5b4ff5c7aef40f29166fd4ad12acbc99c0" +checksum = "9008cd6385b9e161d8229e1f6549dd23c3d022f132a2ea37ac3a10ac4935779b" dependencies = [ "bitvec", "bytecheck", @@ -5200,9 +5272,9 @@ dependencies = [ [[package]] name = "rkyv_derive" -version = "0.7.44" +version = "0.7.45" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a7dddfff8de25e6f62b9d64e6e432bf1c6736c57d20323e15ee10435fbda7c65" +checksum = "503d1d27590a2b0a3a4ca4c94755aa2875657196ecbf401a42eff41d7de532c0" dependencies = [ "proc-macro2", "quote", @@ -5292,9 +5364,9 @@ dependencies = [ [[package]] name = "rust-ini" -version = "0.21.0" +version = "0.21.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0d625ed57d8f49af6cfa514c42e1a71fadcff60eb0b1c517ff82fe41aa025b41" +checksum = "4e310ef0e1b6eeb79169a1171daf9abcb87a2e17c03bee2c4bb100b55c75409f" dependencies = [ "cfg-if", "ordered-multimap", @@ -5350,9 +5422,9 @@ dependencies = [ [[package]] name = "rust_decimal" -version = "1.35.0" +version = "1.36.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1790d1c4c0ca81211399e0e0af16333276f375209e71a37b67698a373db5b47a" +checksum = "b082d80e3e3cc52b2ed634388d436fe1f4de6af5786cc2de9ba9737527bdf555" dependencies = [ "arrayvec", "borsh", @@ -5376,6 +5448,12 @@ version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "08d43f7aa6b08d49f382cde6a7982047c3426db949b1424bc4b7ec9ae12c6ce2" +[[package]] +name = "rustc-hash" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "583034fd73374156e66797ed8e5b0d5690409c9226b22d87cb7f19821c05d152" + [[package]] name = "rustc_version" version = "0.2.3" @@ -5448,6 +5526,8 @@ version = "0.23.12" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c58f8c84392efc0a126acce10fa59ff7b3d2ac06ab451a33f2741989b806b044" dependencies = [ + "aws-lc-rs", + "log", "once_cell", "ring 0.17.8", "rustls-pki-types", @@ -5470,12 +5550,12 @@ dependencies = [ [[package]] name = "rustls-native-certs" -version = "0.7.1" +version = "0.7.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a88d6d420651b496bdd98684116959239430022a115c1240e6c3993be0b15fba" +checksum = "04182dffc9091a404e0fc069ea5cd60e5b866c3adf881eff99a32d048242dffa" dependencies = [ "openssl-probe", - "rustls-pemfile 2.1.2", + "rustls-pemfile 2.1.3", "rustls-pki-types", "schannel", "security-framework", @@ -5492,9 +5572,9 @@ dependencies = [ [[package]] name = "rustls-pemfile" -version = "2.1.2" +version = "2.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "29993a25686778eb88d4189742cd713c9bce943bc54251a33509dc63cbacf73d" +checksum = "196fe16b00e106300d3e45ecfcb764fa292a535d7326a29a5875c579c7417425" dependencies = [ "base64 0.22.1", "rustls-pki-types", @@ -5502,9 +5582,9 @@ dependencies = [ [[package]] name = "rustls-pki-types" -version = "1.7.0" +version = "1.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "976295e77ce332211c0d24d92c0e83e50f5c5f046d11082cea19f3df13a3562d" +checksum = "fc0a2ce646f8655401bb81e7927b812614bd5d91dbc968696be50603510fcaf0" [[package]] name = "rustls-webpki" @@ -5522,6 +5602,7 @@ version = "0.102.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8e6b52d4fda176fd835fdc55a835d4a89b8499cad995885a21149d5ad62f852e" dependencies = [ + "aws-lc-rs", "ring 0.17.8", "rustls-pki-types", "untrusted 0.9.0", @@ -5565,9 +5646,9 @@ checksum = "ece8e78b2f38ec51c51f5d475df0a7187ba5111b2a28bdc761ee05b075d40a71" [[package]] name = "scc" -version = "2.1.6" +version = "2.1.16" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "05ccfb12511cdb770157ace92d7dda771e498445b78f9886e8cdbc5140a4eced" +checksum = "aeb7ac86243095b70a7920639507b71d51a63390d1ba26c4f60a552fbb914a37" dependencies = [ "sdd", ] @@ -5620,9 +5701,9 @@ dependencies = [ [[package]] name = "sdd" -version = "2.1.0" +version = "3.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "177258b64c0faaa9ffd3c65cd3262c2bc7e2588dbbd9c1641d0346145c1bbda8" +checksum = "0495e4577c672de8254beb68d01a9b62d0e8a13c099edecdbedccce3223cd29f" [[package]] name = "seahash" @@ -5746,9 +5827,9 @@ dependencies = [ [[package]] name = "serde" -version = "1.0.204" +version = "1.0.208" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bc76f558e0cbb2a839d37354c575f1dc3fdc6546b5be373ba43d95f231bf7c12" +checksum = "cff085d2cb684faa248efb494c39b68e522822ac0de72ccf08109abde717cfb2" dependencies = [ "serde_derive", ] @@ -5764,20 +5845,20 @@ dependencies = [ [[package]] name = "serde_derive" -version = "1.0.204" +version = "1.0.208" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e0cd7e117be63d3c3678776753929474f3b04a43a080c744d6b0ae2a8c28e222" +checksum = "24008e81ff7613ed8e5ba0cfaf24e2c2f1e5b8a0495711e44fcd4882fca62bcf" dependencies = [ "proc-macro2", "quote", - "syn 2.0.72", + "syn 2.0.75", ] [[package]] name = "serde_json" -version = "1.0.121" +version = "1.0.125" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4ab380d7d9f22ef3f21ad3e6c1ebe8e4fc7a2000ccba2e4d71fc96f15b2cb609" +checksum = "83c8e735a073ccf5be70aa8066aa984eaf2fa000db6c8d0100ae605b366d31ed" dependencies = [ "itoa", "memchr", @@ -5841,7 +5922,7 @@ checksum = "82fe9db325bcef1fbcde82e078a5cc4efdf787e96b3b9cf45b50b529f2083d67" dependencies = [ "proc-macro2", "quote", - "syn 2.0.72", + "syn 2.0.75", ] [[package]] @@ -5990,7 +6071,7 @@ checksum = "3c5e1a9a646d36c3599cd173a41282daf47c44583ad367b8e6837255952e5c67" [[package]] name = "smtp" -version = "0.9.1" +version = "0.9.2" dependencies = [ "ahash 0.8.11", "bincode", @@ -6015,9 +6096,9 @@ dependencies = [ "rand", "rayon", "regex", - "reqwest 0.12.5", + "reqwest 0.12.7", "rustls 0.23.12", - "rustls-pemfile 2.1.2", + "rustls-pemfile 2.1.3", "rustls-pki-types", "serde", "serde_json", @@ -6106,7 +6187,7 @@ checksum = "a8f112729512f8e442d81f95a8a7ddf2b7c6b8a1a6f509a95864142b30cab2d3" [[package]] name = "stalwart-cli" -version = "0.9.1" +version = "0.9.2" dependencies = [ "clap", "console", @@ -6122,7 +6203,7 @@ dependencies = [ "prettytable-rs", "pwhash", "rand", - "reqwest 0.12.5", + "reqwest 0.12.7", "rpassword", "serde", "serde_json", @@ -6137,7 +6218,7 @@ checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f" [[package]] name = "store" -version = "0.9.1" +version = "0.9.2" dependencies = [ "ahash 0.8.11", "arc-swap", @@ -6246,9 +6327,9 @@ dependencies = [ [[package]] name = "syn" -version = "2.0.72" +version = "2.0.75" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dc4b9b9bf2add8093d3f2c0204471e951b2285580335de42f9d2534f3ae7a8af" +checksum = "f6af063034fc1935ede7be0122941bafa9bacb949334d090b77ca98b5817c7d9" dependencies = [ "proc-macro2", "quote", @@ -6264,7 +6345,7 @@ dependencies = [ "proc-macro-error", "proc-macro2", "quote", - "syn 2.0.72", + "syn 2.0.75", ] [[package]] @@ -6278,6 +6359,9 @@ name = "sync_wrapper" version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a7065abeca94b6a8a577f9bd45aa0867a2238b74e8eb67cf10d492bc39351394" +dependencies = [ + "futures-core", +] [[package]] name = "synstructure" @@ -6299,7 +6383,7 @@ checksum = "c8af7666ab7b6390ab78131fb5b0fce11d6b7a6951602017c35fa82800708971" dependencies = [ "proc-macro2", "quote", - "syn 2.0.72", + "syn 2.0.75", ] [[package]] @@ -6382,10 +6466,10 @@ dependencies = [ "num_cpus", "pop3", "rayon", - "reqwest 0.12.5", + "reqwest 0.12.7", "ring 0.17.8", "rustls 0.23.12", - "rustls-pemfile 2.1.2", + "rustls-pemfile 2.1.3", "rustls-pki-types", "serde", "serde_json", @@ -6417,7 +6501,7 @@ checksum = "a4558b58466b9ad7ca0f102865eccc95938dca1a74a856f2b57b6629050da261" dependencies = [ "proc-macro2", "quote", - "syn 2.0.72", + "syn 2.0.75", ] [[package]] @@ -6497,14 +6581,14 @@ checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20" [[package]] name = "tokio" -version = "1.39.2" +version = "1.39.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "daa4fb1bc778bd6f04cbfc4bb2d06a7396a8f299dc33ea1900cedaa316f467b1" +checksum = "9babc99b9923bfa4804bd74722ff02c0381021eafa4db9949217e3be8e84fff5" dependencies = [ "backtrace", "bytes", "libc", - "mio 1.0.1", + "mio", "parking_lot", "pin-project-lite", "signal-hook-registry", @@ -6521,7 +6605,7 @@ checksum = "693d596312e88961bc67d7f1f97af8a70227d9f90c31bba5806eec004978d752" dependencies = [ "proc-macro2", "quote", - "syn 2.0.72", + "syn 2.0.75", ] [[package]] @@ -6646,7 +6730,7 @@ version = "0.21.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6a8534fd7f78b5405e860340ad6575217ce99f38d4d5c8f2442cb5ecb50090e1" dependencies = [ - "indexmap 2.3.0", + "indexmap 2.4.0", "toml_datetime", "winnow", ] @@ -6662,7 +6746,7 @@ dependencies = [ "axum", "base64 0.22.1", "bytes", - "h2 0.4.5", + "h2 0.4.6", "http 1.1.0", "http-body 1.0.1", "http-body-util", @@ -6718,15 +6802,15 @@ dependencies = [ [[package]] name = "tower-layer" -version = "0.3.2" +version = "0.3.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c20c8dbed6283a09604c3e69b4b7eeb54e298b8a600d4d5ecb5ad39de609f1d0" +checksum = "121c2a6cda46980bb0fcd1647ffaf6cd3fc79a013de288782836f6df9c48780e" [[package]] name = "tower-service" -version = "0.3.2" +version = "0.3.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b6bc1c9ce2b5135ac7f93c72918fc37feb872bdc6a5533a8b85eb4b86bfdae52" +checksum = "8df9b6e13f2d32c91b9bd719c00d1958837bc7dec474d94952798cc8e69eeec3" [[package]] name = "tracing" @@ -6747,7 +6831,7 @@ checksum = "34704c8d6ebcbc939824180af020566b01a7c01f80641264eba0999f6c2b6be7" dependencies = [ "proc-macro2", "quote", - "syn 2.0.72", + "syn 2.0.75", ] [[package]] @@ -6761,7 +6845,7 @@ dependencies = [ [[package]] name = "trc" -version = "0.9.1" +version = "0.9.2" dependencies = [ "ahash 0.8.11", "base64 0.22.1", @@ -6770,7 +6854,7 @@ dependencies = [ "mail-auth", "mail-parser", "parking_lot", - "reqwest 0.12.5", + "reqwest 0.12.7", "rtrb", "serde", "serde_json", @@ -6907,9 +6991,9 @@ dependencies = [ [[package]] name = "unicode-properties" -version = "0.1.1" +version = "0.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e4259d9d4425d9f0661581b804cb85fe66a4c631cadd8f490d1c13a35d5d9291" +checksum = "52ea75f83c0137a9b98608359a5f1af8144876eb67bcb1ce837368e906a9f524" [[package]] name = "unicode-script" @@ -6935,9 +7019,9 @@ checksum = "0336d538f7abc86d282a4189614dfaa90810dfc2c6f6427eaf88e16311dd225d" [[package]] name = "unicode-xid" -version = "0.2.4" +version = "0.2.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f962df74c8c05a667b5ee8bcf162993134c104e96440b663c8daa176dc772d8c" +checksum = "229730647fbc343e3a80e463c1db7f78f3855d3f3739bee0dda773c9a037c90a" [[package]] name = "universal-hash" @@ -7004,7 +7088,7 @@ checksum = "06abde3611657adf66d383f00b093d7faecc7fa57071cce2578660c9f1010821" [[package]] name = "utils" -version = "0.9.1" +version = "0.9.2" dependencies = [ "ahash 0.8.11", "base64 0.22.1", @@ -7023,10 +7107,10 @@ dependencies = [ "rand", "rcgen 0.13.1", "regex", - "reqwest 0.12.5", + "reqwest 0.12.7", "ring 0.17.8", "rustls 0.23.12", - "rustls-pemfile 2.1.2", + "rustls-pemfile 2.1.3", "rustls-pki-types", "serde", "serde_json", @@ -7098,34 +7182,35 @@ checksum = "b8dad83b4f25e74f184f64c43b150b91efe7647395b42289f38e50566d82855b" [[package]] name = "wasm-bindgen" -version = "0.2.92" +version = "0.2.93" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4be2531df63900aeb2bca0daaaddec08491ee64ceecbee5076636a3b026795a8" +checksum = "a82edfc16a6c469f5f44dc7b571814045d60404b55a0ee849f9bcfa2e63dd9b5" dependencies = [ "cfg-if", + "once_cell", "wasm-bindgen-macro", ] [[package]] name = "wasm-bindgen-backend" -version = "0.2.92" +version = "0.2.93" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "614d787b966d3989fa7bb98a654e369c762374fd3213d212cfc0251257e747da" +checksum = "9de396da306523044d3302746f1208fa71d7532227f15e347e2d93e4145dd77b" dependencies = [ "bumpalo", "log", "once_cell", "proc-macro2", "quote", - "syn 2.0.72", + "syn 2.0.75", "wasm-bindgen-shared", ] [[package]] name = "wasm-bindgen-futures" -version = "0.4.42" +version = "0.4.43" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "76bc14366121efc8dbb487ab05bcc9d346b3b5ec0eaa76e46594cabbe51762c0" +checksum = "61e9300f63a621e96ed275155c108eb6f843b6a26d053f122ab69724559dc8ed" dependencies = [ "cfg-if", "js-sys", @@ -7135,9 +7220,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-macro" -version = "0.2.92" +version = "0.2.93" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a1f8823de937b71b9460c0c34e25f3da88250760bec0ebac694b49997550d726" +checksum = "585c4c91a46b072c92e908d99cb1dcdf95c5218eeb6f3bf1efa991ee7a68cccf" dependencies = [ "quote", "wasm-bindgen-macro-support", @@ -7145,22 +7230,22 @@ dependencies = [ [[package]] name = "wasm-bindgen-macro-support" -version = "0.2.92" +version = "0.2.93" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e94f17b526d0a461a191c78ea52bbce64071ed5c04c9ffe424dcb38f74171bb7" +checksum = "afc340c74d9005395cf9dd098506f7f44e38f2b4a21c6aaacf9a105ea5e1e836" dependencies = [ "proc-macro2", "quote", - "syn 2.0.72", + "syn 2.0.75", "wasm-bindgen-backend", "wasm-bindgen-shared", ] [[package]] name = "wasm-bindgen-shared" -version = "0.2.92" +version = "0.2.93" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "af190c94f2773fdb3729c55b007a722abb5384da03bc0986df4c289bf5567e96" +checksum = "c62a0a307cb4a311d3a07867860911ca130c3494e8c2719593806c08bc5d0484" [[package]] name = "wasm-streams" @@ -7177,9 +7262,9 @@ dependencies = [ [[package]] name = "web-sys" -version = "0.3.69" +version = "0.3.70" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "77afa9a11836342370f4817622a2f0f418b134426d91a82dfb48f532d2ec13ef" +checksum = "26fdeaafd9bd129f65e7c031593c24d62186301e0c72c8978fa1678be7d532c0" dependencies = [ "js-sys", "wasm-bindgen", @@ -7267,11 +7352,11 @@ checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" [[package]] name = "winapi-util" -version = "0.1.8" +version = "0.1.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4d4cc384e1e73b93bafa6fb4f1df8c41695c8a91cf9c4c64358067d15a7b6c6b" +checksum = "cf221c93e13a30d793f7645a0e7762c55d169dbb0a49671918a2319d289b10bb" dependencies = [ - "windows-sys 0.52.0", + "windows-sys 0.59.0", ] [[package]] @@ -7299,6 +7384,36 @@ dependencies = [ "windows-targets 0.52.6", ] +[[package]] +name = "windows-registry" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e400001bb720a623c1c69032f8e3e4cf09984deec740f007dd2b03ec864804b0" +dependencies = [ + "windows-result", + "windows-strings", + "windows-targets 0.52.6", +] + +[[package]] +name = "windows-result" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1d1043d8214f791817bab27572aaa8af63732e11bf84aa21a45a78d6c317ae0e" +dependencies = [ + "windows-targets 0.52.6", +] + +[[package]] +name = "windows-strings" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4cd9b125c486025df0eabcb585e62173c6c9eddcec5d117d3b6e8c30e2ee4d10" +dependencies = [ + "windows-result", + "windows-targets 0.52.6", +] + [[package]] name = "windows-sys" version = "0.48.0" @@ -7317,6 +7432,15 @@ dependencies = [ "windows-targets 0.52.6", ] +[[package]] +name = "windows-sys" +version = "0.59.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e38bc4d79ed67fd075bcc251a1c39b32a1776bbe92e5bef1f0bf1f8c531853b" +dependencies = [ + "windows-targets 0.52.6", +] + [[package]] name = "windows-targets" version = "0.48.5" @@ -7457,16 +7581,6 @@ dependencies = [ "windows-sys 0.48.0", ] -[[package]] -name = "winreg" -version = "0.52.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a277a57398d4bfa075df44f501a17cfdf8542d224f0d36095a2adc7aee4ef0a5" -dependencies = [ - "cfg-if", - "windows-sys 0.48.0", -] - [[package]] name = "write16" version = "1.0.0" @@ -7535,9 +7649,9 @@ dependencies = [ [[package]] name = "xml-rs" -version = "0.8.20" +version = "0.8.21" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "791978798f0597cfc70478424c2b4fdc2b7a8024aaff78497ef00f24ef674193" +checksum = "539a77ee7c0de333dcc6da69b177380a0b81e0dacfa4f7344c465a36871ee601" [[package]] name = "xxhash-rust" @@ -7574,38 +7688,18 @@ checksum = "28cc31741b18cb6f1d5ff12f5b7523e3d6eb0852bbbad19d73905511d9849b95" dependencies = [ "proc-macro2", "quote", - "syn 2.0.72", + "syn 2.0.75", "synstructure 0.13.1", ] -[[package]] -name = "zerocopy" -version = "0.6.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "854e949ac82d619ee9a14c66a1b674ac730422372ccb759ce0c39cabcf2bf8e6" -dependencies = [ - "byteorder", - "zerocopy-derive 0.6.6", -] - [[package]] name = "zerocopy" version = "0.7.35" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1b9b4fd18abc82b8136838da5d50bae7bdea537c574d8dc1a34ed098d6c166f0" dependencies = [ - "zerocopy-derive 0.7.35", -] - -[[package]] -name = "zerocopy-derive" -version = "0.6.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "125139de3f6b9d625c39e2efdd73d41bdac468ccd556556440e322be0e1bbd91" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.72", + "byteorder", + "zerocopy-derive", ] [[package]] @@ -7616,7 +7710,7 @@ checksum = "fa4f8080344d4671fb4e831a13ad1e68092748387dfc4f55e356242fae12ce3e" dependencies = [ "proc-macro2", "quote", - "syn 2.0.72", + "syn 2.0.75", ] [[package]] @@ -7636,7 +7730,7 @@ checksum = "0ea7b4a3637ea8669cedf0f1fd5c286a17f3de97b8dd5a70a6c167a1730e63a5" dependencies = [ "proc-macro2", "quote", - "syn 2.0.72", + "syn 2.0.75", "synstructure 0.13.1", ] @@ -7657,7 +7751,7 @@ checksum = "ce36e65b0d2999d2aafac989fb249189a141aee1f53c612c1f37d72631959f69" dependencies = [ "proc-macro2", "quote", - "syn 2.0.72", + "syn 2.0.75", ] [[package]] @@ -7679,14 +7773,14 @@ checksum = "6eafa6dfb17584ea3e2bd6e76e0cc15ad7af12b09abdd1ca55961bed9b1063c6" dependencies = [ "proc-macro2", "quote", - "syn 2.0.72", + "syn 2.0.75", ] [[package]] name = "zip" -version = "2.1.6" +version = "2.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "40dd8c92efc296286ce1fbd16657c5dbefff44f1b4ca01cc5f517d8b7b3d3e2e" +checksum = "dc5e4288ea4057ae23afc69a4472434a87a2495cafce6632fd1c4ec9f5cf3494" dependencies = [ "aes", "arbitrary", @@ -7698,7 +7792,7 @@ dependencies = [ "displaydoc", "flate2", "hmac 0.12.1", - "indexmap 2.3.0", + "indexmap 2.4.0", "lzma-rs", "memchr", "pbkdf2", @@ -7736,18 +7830,18 @@ dependencies = [ [[package]] name = "zstd-safe" -version = "7.2.0" +version = "7.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fa556e971e7b568dc775c136fc9de8c779b1c2fc3a63defaafadffdbd3181afa" +checksum = "54a3ab4db68cea366acc5c897c7b4d4d1b8994a9cd6e6f841f8964566a419059" dependencies = [ "zstd-sys", ] [[package]] name = "zstd-sys" -version = "2.0.12+zstd.1.5.6" +version = "2.0.13+zstd.1.5.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0a4e40c320c3cb459d9a9ff6de98cff88f4751ee9275d140e2be94a2b74e4c13" +checksum = "38ff0f21cfee8f97d94cef41359e0c89aa6113028ab0291aa8ca0038995a95aa" dependencies = [ "cc", "pkg-config", diff --git a/crates/cli/Cargo.toml b/crates/cli/Cargo.toml index 9cc8418b..952f2b9e 100644 --- a/crates/cli/Cargo.toml +++ b/crates/cli/Cargo.toml @@ -5,7 +5,7 @@ authors = ["Stalwart Labs Ltd. "] license = "AGPL-3.0-only OR LicenseRef-SEL" repository = "https://github.com/stalwartlabs/cli" homepage = "https://github.com/stalwartlabs/cli" -version = "0.9.1" +version = "0.9.2" edition = "2021" readme = "README.md" resolver = "2" diff --git a/crates/common/Cargo.toml b/crates/common/Cargo.toml index 9595a5ff..bf9773de 100644 --- a/crates/common/Cargo.toml +++ b/crates/common/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "common" -version = "0.9.1" +version = "0.9.2" edition = "2021" resolver = "2" diff --git a/crates/common/src/enterprise/config.rs b/crates/common/src/enterprise/config.rs index 9d259a15..63a516a1 100644 --- a/crates/common/src/enterprise/config.rs +++ b/crates/common/src/enterprise/config.rs @@ -57,10 +57,10 @@ impl Enterprise { Some(Enterprise { license, undelete_period: config - .property_or_default::>("storage.undelete.hold-for", "false") + .property_or_default::>("storage.undelete.retention", "false") .unwrap_or_default(), trace_hold_period: config - .property_or_default::>("tracing.history.hold-for", "90d") + .property_or_default::>("tracing.history.retention", "90d") .unwrap_or(Some(Duration::from_secs(90 * 24 * 60 * 60))), trace_store: config .value("tracing.history.store") diff --git a/crates/common/src/telemetry/tracers/store.rs b/crates/common/src/telemetry/tracers/store.rs index d36c2b5e..845819df 100644 --- a/crates/common/src/telemetry/tracers/store.rs +++ b/crates/common/src/telemetry/tracers/store.rs @@ -181,7 +181,8 @@ impl TracingStore for Store { from_span_id: u64, to_span_id: u64, ) -> trc::Result> { - let mut spans = AHashSet::new(); + let mut spans = SpanCollector::Empty; + let num_params = params.len(); for (param_num, param) in params.iter().enumerate() { let (value, exact_len) = match param { @@ -202,7 +203,7 @@ impl TracingStore for Store { } }; - let mut param_spans = AHashSet::new(); + let mut param_spans = SpanCollector::new(num_params); self.iterate( IterateParams::new( ValueKey::from(ValueClass::Trace(TraceClass::Index { @@ -236,17 +237,12 @@ impl TracingStore for Store { if param_num == 0 { spans = param_spans; - } else { - spans = spans.intersection(¶m_spans).copied().collect(); - if spans.is_empty() { - break; - } + } else if spans.intersect(param_spans) { + return Ok(Vec::new()); } } - let mut spans: Vec = spans.into_iter().collect(); - spans.sort_unstable(); - Ok(spans) + Ok(spans.into_vec()) } async fn purge_spans(&self, period: Duration) -> trc::Result<()> { @@ -316,6 +312,57 @@ impl TracingStore for Store { } } +enum SpanCollector { + Vec(Vec), + HashSet(AHashSet), + Empty, +} + +impl SpanCollector { + fn new(num_params: usize) -> Self { + if num_params == 1 { + Self::Vec(Vec::new()) + } else { + Self::HashSet(AHashSet::new()) + } + } + + fn insert(&mut self, span_id: u64) { + match self { + Self::Vec(vec) => vec.push(span_id), + Self::HashSet(set) => { + set.insert(span_id); + } + _ => unreachable!(), + } + } + + fn into_vec(self) -> Vec { + match self { + Self::Vec(mut vec) => { + vec.sort_unstable(); + vec + } + Self::HashSet(set) => { + let mut vec: Vec = set.into_iter().collect(); + vec.sort_unstable(); + vec + } + Self::Empty => Vec::new(), + } + } + + fn intersect(&mut self, other_span: Self) -> bool { + match (self, other_span) { + (Self::HashSet(set), Self::HashSet(other_set)) => { + set.retain(|span_id| other_set.contains(span_id)); + set.is_empty() + } + _ => unreachable!(), + } + } +} + impl StoreTracer { pub fn default_events() -> impl IntoIterator { EventType::variants().into_iter().filter(|event| { @@ -339,7 +386,7 @@ impl StoreTracer { | EventType::MailAuth(_) | EventType::Queue( QueueEvent::QueueMessage - | QueueEvent::QueueMessageSubmission + | QueueEvent::QueueMessageAuthenticated | QueueEvent::QueueReport | QueueEvent::QueueDsn | QueueEvent::QueueAutogenerated diff --git a/crates/common/src/telemetry/webhooks/mod.rs b/crates/common/src/telemetry/webhooks/mod.rs index a6e5c034..edf6c290 100644 --- a/crates/common/src/telemetry/webhooks/mod.rs +++ b/crates/common/src/telemetry/webhooks/mod.rs @@ -20,7 +20,8 @@ use store::write::now; use tokio::sync::mpsc; use trc::{ ipc::subscriber::{EventBatch, SubscriberBuilder}, - ServerEvent, TelemetryEvent, + serializers::json::JsonEventSerializer, + Event, EventDetails, ServerEvent, TelemetryEvent, }; use super::LONG_SLUMBER; @@ -97,7 +98,7 @@ pub(crate) fn spawn_webhook_tracer(builder: SubscriberBuilder, settings: Webhook #[derive(Serialize)] struct EventWrapper { - events: EventBatch, + events: JsonEventSerializer>>>, } fn spawn_webhook_handler( @@ -108,12 +109,14 @@ fn spawn_webhook_handler( ) { tokio::spawn(async move { in_flight.store(true, Ordering::Relaxed); - let wrapper = EventWrapper { events }; + let wrapper = EventWrapper { + events: JsonEventSerializer::new(events).with_id(), + }; if let Err(err) = post_webhook_events(&settings, &wrapper).await { trc::event!(Telemetry(TelemetryEvent::WebhookError), Details = err); - if webhook_tx.send(wrapper.events).await.is_err() { + if webhook_tx.send(wrapper.events.into_inner()).await.is_err() { trc::event!( Server(ServerEvent::ThreadError), Details = "Failed to send failed webhook events back to main thread", diff --git a/crates/directory/Cargo.toml b/crates/directory/Cargo.toml index 2f6c814d..5124bbbc 100644 --- a/crates/directory/Cargo.toml +++ b/crates/directory/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "directory" -version = "0.9.1" +version = "0.9.2" edition = "2021" resolver = "2" diff --git a/crates/directory/src/backend/internal/manage.rs b/crates/directory/src/backend/internal/manage.rs index 7adb50b7..710245b5 100644 --- a/crates/directory/src/backend/internal/manage.rs +++ b/crates/directory/src/backend/internal/manage.rs @@ -1073,6 +1073,10 @@ pub fn unsupported(details: impl Into) -> trc::Error { trc::ManageEvent::NotSupported.ctx(trc::Key::Details, details) } +pub fn enterprise() -> trc::Error { + trc::ManageEvent::NotSupported.ctx(trc::Key::Details, "Enterprise feature") +} + pub fn error(details: impl Into, reason: Option>) -> trc::Error { trc::ManageEvent::Error .ctx(trc::Key::Details, details) diff --git a/crates/imap/Cargo.toml b/crates/imap/Cargo.toml index bb2836d7..77258fcb 100644 --- a/crates/imap/Cargo.toml +++ b/crates/imap/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "imap" -version = "0.9.1" +version = "0.9.2" edition = "2021" resolver = "2" diff --git a/crates/jmap/Cargo.toml b/crates/jmap/Cargo.toml index 26c1b256..cff254db 100644 --- a/crates/jmap/Cargo.toml +++ b/crates/jmap/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "jmap" -version = "0.9.1" +version = "0.9.2" edition = "2021" resolver = "2" diff --git a/crates/jmap/src/api/http.rs b/crates/jmap/src/api/http.rs index 92d2cb9a..0e1b94d7 100644 --- a/crates/jmap/src/api/http.rs +++ b/crates/jmap/src/api/http.rs @@ -279,11 +279,45 @@ impl JMAP { } // Authenticate user - let (_, access_token) = self.authenticate_headers(&req, &session).await?; - let body = fetch_body(&mut req, 1024 * 1024, session.session_id).await; - return self - .handle_api_manage_request(&req, body, access_token, &session) - .await; + match self.authenticate_headers(&req, &session).await { + Ok((_, access_token)) => { + let body = fetch_body(&mut req, 1024 * 1024, session.session_id).await; + return self + .handle_api_manage_request(&req, body, access_token, &session) + .await; + } + Err(err) => { + #[cfg(feature = "enterprise")] + { + // SPDX-SnippetBegin + // SPDX-FileCopyrightText: 2020 Stalwart Labs Ltd + // SPDX-License-Identifier: LicenseRef-SEL + + // Eventsource does not support authentication, validate the token instead + if err.matches(trc::EventType::Auth(trc::AuthEvent::Failed)) + && self.core.is_enterprise_edition() + { + if let Some(token) = + req.uri().path().strip_prefix("/api/tracing/live/") + { + let (account_id, _, _) = + self.validate_access_token("live_tracing", token).await?; + + return self + .handle_tracing_api_request( + &req, + vec!["", "live"], + account_id, + ) + .await; + } + } + // SPDX-SnippetEnd + } + + return Err(err); + } + } } "mail" => { if req.method() == Method::GET diff --git a/crates/jmap/src/api/management/enterprise/tracing.rs b/crates/jmap/src/api/management/enterprise/tracing.rs index d69c682c..6e630c40 100644 --- a/crates/jmap/src/api/management/enterprise/tracing.rs +++ b/crates/jmap/src/api/management/enterprise/tracing.rs @@ -22,7 +22,8 @@ use serde_json::json; use store::ahash::{AHashMap, AHashSet}; use trc::{ ipc::{bitset::Bitset, subscriber::SubscriberBuilder}, - Key, Value, + serializers::json::JsonEventSerializer, + DeliveryEvent, EventType, Key, QueueEvent, Value, }; use utils::{snowflake::SnowflakeIdGenerator, url_params::UrlParams}; @@ -39,12 +40,13 @@ impl JMAP { &self, req: &HttpRequest, path: Vec<&str>, + account_id: u32, ) -> trc::Result { let params = UrlParams::new(req.uri().query()); match ( - path.get(2).copied().unwrap(), - path.get(3).copied(), + path.get(1).copied().unwrap_or_default(), + path.get(2).copied(), req.method(), ) { ("spans", None, &Method::GET) => { @@ -97,16 +99,14 @@ impl JMAP { .map(|t| t.into_inner()) .and_then(SnowflakeIdGenerator::from_timestamp) .unwrap_or(0); - let span_ids = self + let values = params.get("values").is_some(); + let store = self .core .enterprise .as_ref() .and_then(|e| e.trace_store.as_ref()) - .ok_or_else(|| { - manage::error("Unavailable", "No tracing store has been configured".into()) - })? - .query_spans(&tracing_query, after, before) - .await?; + .ok_or_else(|| manage::unsupported("No tracing store has been configured"))?; + let span_ids = store.query_spans(&tracing_query, after, before).await?; let (total, span_ids) = if limit > 0 { let offset = page.saturating_sub(1) * limit; @@ -118,13 +118,41 @@ impl JMAP { (span_ids.len(), span_ids) }; - Ok(JsonResponse::new(json!({ - "data": { - "items": span_ids, - "total": total, - }, - })) - .into_http_response()) + if values && !span_ids.is_empty() { + let mut values = Vec::with_capacity(span_ids.len()); + + for span_id in span_ids { + for event in store.get_span(span_id).await? { + if matches!( + event.inner.typ, + EventType::Delivery(DeliveryEvent::AttemptStart) + | EventType::Queue( + QueueEvent::QueueMessage + | QueueEvent::QueueMessageAuthenticated + ) + ) { + values.push(event); + break; + } + } + } + + Ok(JsonResponse::new(json!({ + "data": { + "items": JsonEventSerializer::new(values).with_spans(), + "total": total, + }, + })) + .into_http_response()) + } else { + Ok(JsonResponse::new(json!({ + "data": { + "items": span_ids, + "total": total, + }, + })) + .into_http_response()) + } } ("span", id, &Method::GET) => { let store = self @@ -132,9 +160,7 @@ impl JMAP { .enterprise .as_ref() .and_then(|e| e.trace_store.as_ref()) - .ok_or_else(|| { - manage::error("Unavailable", "No tracing store has been configured".into()) - })?; + .ok_or_else(|| manage::unsupported("No tracing store has been configured"))?; let mut events = Vec::new(); for span_id in id @@ -143,21 +169,45 @@ impl JMAP { .split(',') { if let Ok(span_id) = span_id.parse::() { - events.push(store.get_span(span_id).await?); + events.push( + JsonEventSerializer::new(store.get_span(span_id).await?) + .with_description() + .with_explanation(), + ); + } else { + events.push(JsonEventSerializer::new(Vec::new())); } } - Ok(JsonResponse::new(json!({ - "data": events, - })) - .into_http_response()) + if events.len() == 1 && id.is_some() { + Ok(JsonResponse::new(json!({ + "data": events.into_iter().next().unwrap(), + })) + .into_http_response()) + } else { + Ok(JsonResponse::new(json!({ + "data": events, + })) + .into_http_response()) + } } - ("live", None, &Method::GET) => { - let mut filters = AHashMap::new(); + ("live", Some("token"), &Method::GET) => { + // Issue a live tracing token valid for 60 seconds + + Ok(JsonResponse::new(json!({ + "data": self.issue_custom_token(account_id, "live_tracing", "web", 60).await?, + })) + .into_http_response()) + } + ("live", _, &Method::GET) => { + let mut key_filters = AHashMap::new(); + let mut filter = None; for (key, value) in params.into_inner() { - if let Some(key) = Key::try_parse(key.as_ref()) { - filters.insert(key, value.into_owned()); + if key == "filter" { + filter = value.into_owned().into(); + } else if let Some(key) = Key::try_parse(key.to_ascii_lowercase().as_str()) { + key_filters.insert(key, value.into_owned()); } } @@ -189,7 +239,7 @@ impl JMAP { match tokio::time::timeout(timeout, rx.recv()).await { Ok(Some(event_batch)) => { for event in event_batch { - if filters.is_empty() + if (filter.is_none() && key_filters.is_empty()) || event .span_id() .map_or(false, |span_id| active_span_ids.contains(&span_id)) @@ -202,34 +252,32 @@ impl JMAP { .iter() .chain(event.inner.span.as_ref().map_or(([]).iter(), |s| s.keys.iter())) { - if let Some(needle) = filters.get(key) { + if let Some(needle) = key_filters.get(key).or(filter.as_ref()) { let matches = match value { Value::Static(haystack) => haystack.contains(needle), Value::String(haystack) => haystack.contains(needle), - Value::UInt(haystack) => haystack.to_string().contains(needle), - Value::Int(haystack) => haystack.to_string().contains(needle), - Value::Float(haystack) => haystack.to_string().contains(needle), Value::Timestamp(haystack) => { DateTime::from_timestamp(*haystack as i64) .to_rfc3339() .contains(needle) } - Value::Duration(haystack) => { - haystack.to_string().contains(needle) - } - Value::Bytes(haystack) => std::str::from_utf8(haystack) - .unwrap_or_default() - .contains(needle), Value::Bool(true) => needle == "true", Value::Bool(false) => needle == "false", Value::Ipv4(haystack) => haystack.to_string().contains(needle), Value::Ipv6(haystack) => haystack.to_string().contains(needle), - Value::Event(_) | Value::Array(_) | Value::None => false, + Value::Event(_) | + Value::Array(_) | + Value::UInt(_) | + Value::Int(_) | + Value::Float(_) | + Value::Duration(_) | + Value::Bytes(_) | + Value::None => false, }; if matches { matched_keys.insert(*key); - if matched_keys.len() == filters.len() { + if filter.is_some() || matched_keys.len() == key_filters.len() { if let Some(span_id) = event.span_id() { active_span_ids.insert(span_id); } @@ -254,10 +302,12 @@ impl JMAP { last_message = Instant::now(); yield Ok(Frame::data(Bytes::from(format!( "event: state\ndata: {}\n\n", - serde_json::to_string(&events).unwrap() + serde_json::to_string( + &JsonEventSerializer::new(std::mem::take(&mut events)) + .with_description() + .with_explanation()).unwrap_or_default() )))); - events.clear(); ping_interval } else { throttle - elapsed diff --git a/crates/jmap/src/api/management/log.rs b/crates/jmap/src/api/management/log.rs index 9ba0eade..1083a009 100644 --- a/crates/jmap/src/api/management/log.rs +++ b/crates/jmap/src/api/management/log.rs @@ -21,7 +21,9 @@ use crate::{ struct LogEntry { timestamp: String, level: String, - message: String, + event: String, + event_id: String, + details: String, } impl JMAP { @@ -118,12 +120,15 @@ impl LogEntry { fn from_line(line: &str) -> Option { let (timestamp, rest) = line.split_once(' ')?; let timestamp = DateTime::parse_from_rfc3339(timestamp).ok()?; - let (level, message) = rest.trim().split_once(' ')?; - let message = message.split_once(": ").map_or(message, |(_, v)| v); + let (level, rest) = rest.trim().split_once(' ')?; + let (event, rest) = rest.trim().split_once(" (")?; + let (event_id, details) = rest.split_once(")")?; Some(Self { timestamp: timestamp.to_rfc3339_opts(chrono::SecondsFormat::Secs, true), level: level.to_string(), - message: message.to_string(), + event: event.to_string(), + event_id: event_id.to_string(), + details: details.trim().to_string(), }) } } diff --git a/crates/jmap/src/api/management/mod.rs b/crates/jmap/src/api/management/mod.rs index 37d0de66..ce167c91 100644 --- a/crates/jmap/src/api/management/mod.rs +++ b/crates/jmap/src/api/management/mod.rs @@ -83,7 +83,7 @@ impl JMAP { // SPDX-FileCopyrightText: 2020 Stalwart Labs Ltd // SPDX-License-Identifier: LicenseRef-SEL #[cfg(feature = "enterprise")] - "tracing" if is_superuser && self.core.is_enterprise_edition() => { + "tracing" if is_superuser => { // WARNING: TAMPERING WITH THIS FUNCTION IS STRICTLY PROHIBITED // Any attempt to modify, bypass, or disable this license validation mechanism // constitutes a severe violation of the Stalwart Enterprise License Agreement. @@ -93,7 +93,12 @@ impl JMAP { // violators to the fullest extent of the law, including but not limited to claims // for copyright infringement, breach of contract, and fraud. - self.handle_tracing_api_request(req, path).await + if self.core.is_enterprise_edition() { + self.handle_tracing_api_request(req, path, access_token.primary_id()) + .await + } else { + Err(manage::enterprise()) + } } // SPDX-SnippetEnd _ => Err(trc::ResourceEvent::NotFound.into_err()), diff --git a/crates/jmap/src/api/management/stores.rs b/crates/jmap/src/api/management/stores.rs index c10988ba..c8066441 100644 --- a/crates/jmap/src/api/management/stores.rs +++ b/crates/jmap/src/api/management/stores.rs @@ -6,7 +6,7 @@ use base64::{engine::general_purpose::URL_SAFE_NO_PAD, Engine}; use common::manager::webadmin::Resource; -use directory::backend::internal::manage::ManageDirectory; +use directory::backend::internal::manage::{self, ManageDirectory}; use hyper::Method; use serde_json::json; use utils::url_params::UrlParams; @@ -123,7 +123,7 @@ impl JMAP { // SPDX-FileCopyrightText: 2020 Stalwart Labs Ltd // SPDX-License-Identifier: LicenseRef-SEL #[cfg(feature = "enterprise")] - (Some("undelete"), _, _, _) if self.core.is_enterprise_edition() => { + (Some("undelete"), _, _, _) => { // WARNING: TAMPERING WITH THIS FUNCTION IS STRICTLY PROHIBITED // Any attempt to modify, bypass, or disable this license validation mechanism // constitutes a severe violation of the Stalwart Enterprise License Agreement. @@ -133,8 +133,12 @@ impl JMAP { // violators to the fullest extent of the law, including but not limited to claims // for copyright infringement, breach of contract, and fraud. - self.handle_undelete_api_request(req, path, body, session) - .await + if self.core.is_enterprise_edition() { + self.handle_undelete_api_request(req, path, body, session) + .await + } else { + Err(manage::enterprise()) + } } // SPDX-SnippetEnd _ => Err(trc::ResourceEvent::NotFound.into_err()), diff --git a/crates/jmap/src/auth/oauth/token.rs b/crates/jmap/src/auth/oauth/token.rs index e65a1c80..1817399c 100644 --- a/crates/jmap/src/auth/oauth/token.rs +++ b/crates/jmap/src/auth/oauth/token.rs @@ -232,6 +232,26 @@ impl JMAP { }) } + pub async fn issue_custom_token( + &self, + account_id: u32, + grant_type: &str, + client_id: &str, + expiry_in: u64, + ) -> trc::Result { + self.encode_access_token( + grant_type, + account_id, + &self + .password_hash(account_id) + .await + .map_err(|err| trc::StoreEvent::UnexpectedError.into_err().details(err))?, + client_id, + expiry_in, + ) + .map_err(|err| trc::StoreEvent::UnexpectedError.into_err().details(err)) + } + fn encode_access_token( &self, grant_type: &str, diff --git a/crates/main/Cargo.toml b/crates/main/Cargo.toml index 096e46e3..856326c7 100644 --- a/crates/main/Cargo.toml +++ b/crates/main/Cargo.toml @@ -7,7 +7,7 @@ homepage = "https://stalw.art" keywords = ["imap", "jmap", "smtp", "email", "mail", "server"] categories = ["email"] license = "AGPL-3.0-only OR LicenseRef-SEL" -version = "0.9.1" +version = "0.9.2" edition = "2021" resolver = "2" diff --git a/crates/managesieve/Cargo.toml b/crates/managesieve/Cargo.toml index c9a259d1..8fa35009 100644 --- a/crates/managesieve/Cargo.toml +++ b/crates/managesieve/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "managesieve" -version = "0.9.1" +version = "0.9.2" edition = "2021" resolver = "2" diff --git a/crates/nlp/Cargo.toml b/crates/nlp/Cargo.toml index a3270d0e..73d0bda0 100644 --- a/crates/nlp/Cargo.toml +++ b/crates/nlp/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "nlp" -version = "0.9.1" +version = "0.9.2" edition = "2021" resolver = "2" diff --git a/crates/pop3/Cargo.toml b/crates/pop3/Cargo.toml index a99d11b9..1d58ad43 100644 --- a/crates/pop3/Cargo.toml +++ b/crates/pop3/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "pop3" -version = "0.9.1" +version = "0.9.2" edition = "2021" resolver = "2" diff --git a/crates/smtp/Cargo.toml b/crates/smtp/Cargo.toml index 251bb8de..b732077c 100644 --- a/crates/smtp/Cargo.toml +++ b/crates/smtp/Cargo.toml @@ -7,7 +7,7 @@ homepage = "https://stalw.art/smtp" keywords = ["smtp", "email", "mail", "server"] categories = ["email"] license = "AGPL-3.0-only OR LicenseRef-SEL" -version = "0.9.1" +version = "0.9.2" edition = "2021" resolver = "2" diff --git a/crates/smtp/src/queue/spool.rs b/crates/smtp/src/queue/spool.rs index 554b82ee..d2d07b73 100644 --- a/crates/smtp/src/queue/spool.rs +++ b/crates/smtp/src/queue/spool.rs @@ -228,7 +228,7 @@ impl Message { trc::event!( Queue(match source { - MessageSource::Authenticated => trc::QueueEvent::QueueMessageSubmission, + MessageSource::Authenticated => trc::QueueEvent::QueueMessageAuthenticated, MessageSource::Unauthenticated => trc::QueueEvent::QueueMessage, MessageSource::Dsn => trc::QueueEvent::QueueDsn, MessageSource::Report => trc::QueueEvent::QueueReport, diff --git a/crates/store/Cargo.toml b/crates/store/Cargo.toml index 5fddab80..0b7566e8 100644 --- a/crates/store/Cargo.toml +++ b/crates/store/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "store" -version = "0.9.1" +version = "0.9.2" edition = "2021" resolver = "2" diff --git a/crates/trc/Cargo.toml b/crates/trc/Cargo.toml index 71a29518..172b40fe 100644 --- a/crates/trc/Cargo.toml +++ b/crates/trc/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "trc" -version = "0.9.1" +version = "0.9.2" edition = "2021" resolver = "2" diff --git a/crates/trc/src/event/description.rs b/crates/trc/src/event/description.rs index 69dcb191..a11c43b4 100644 --- a/crates/trc/src/event/description.rs +++ b/crates/trc/src/event/description.rs @@ -52,12 +52,58 @@ impl EventType { EventType::MessageIngest(event) => event.description(), } } + + pub fn explain(&self) -> &'static str { + match self { + EventType::Store(event) => event.explain(), + EventType::Jmap(event) => event.explain(), + EventType::Imap(event) => event.explain(), + EventType::ManageSieve(event) => event.explain(), + EventType::Pop3(event) => event.explain(), + EventType::Smtp(event) => event.explain(), + EventType::Network(event) => event.explain(), + EventType::Limit(event) => event.explain(), + EventType::Manage(event) => event.explain(), + EventType::Auth(event) => event.explain(), + EventType::Config(event) => event.explain(), + EventType::Resource(event) => event.explain(), + EventType::Sieve(event) => event.explain(), + EventType::Spam(event) => event.explain(), + EventType::Server(event) => event.explain(), + EventType::Purge(event) => event.explain(), + EventType::Eval(event) => event.explain(), + EventType::Acme(event) => event.explain(), + EventType::Http(event) => event.explain(), + EventType::Arc(event) => event.explain(), + EventType::Dkim(event) => event.explain(), + EventType::Dmarc(event) => event.explain(), + EventType::Iprev(event) => event.explain(), + EventType::Dane(event) => event.explain(), + EventType::Spf(event) => event.explain(), + EventType::MailAuth(event) => event.explain(), + EventType::Tls(event) => event.explain(), + EventType::PushSubscription(event) => event.explain(), + EventType::Cluster(event) => event.explain(), + EventType::Housekeeper(event) => event.explain(), + EventType::FtsIndex(event) => event.explain(), + EventType::Milter(event) => event.explain(), + EventType::MtaHook(event) => event.explain(), + EventType::Delivery(event) => event.explain(), + EventType::Queue(event) => event.explain(), + EventType::TlsRpt(event) => event.explain(), + EventType::MtaSts(event) => event.explain(), + EventType::IncomingReport(event) => event.explain(), + EventType::OutgoingReport(event) => event.explain(), + EventType::Telemetry(event) => event.explain(), + EventType::MessageIngest(event) => event.explain(), + } + } } impl HttpEvent { pub fn description(&self) -> &'static str { match self { - HttpEvent::Error => "An HTTP error occurred", + HttpEvent::Error => "HTTP error occurred", HttpEvent::RequestUrl => "HTTP request URL", HttpEvent::RequestBody => "HTTP request body", HttpEvent::ResponseBody => "HTTP response body", @@ -66,6 +112,18 @@ impl HttpEvent { HttpEvent::ConnectionEnd => "HTTP connection ended", } } + + pub fn explain(&self) -> &'static str { + match self { + HttpEvent::Error => "An error occurred during an HTTP request", + HttpEvent::RequestUrl => "The URL of an HTTP request", + HttpEvent::RequestBody => "The body of an HTTP request", + HttpEvent::ResponseBody => "The body of an HTTP response", + HttpEvent::XForwardedMissing => "The X-Forwarded-For header is missing", + HttpEvent::ConnectionStart => "An HTTP connection was started", + HttpEvent::ConnectionEnd => "An HTTP connection was ended", + } + } } impl ClusterEvent { @@ -87,6 +145,25 @@ impl ClusterEvent { ClusterEvent::Error => "A cluster error occurred", } } + + pub fn explain(&self) -> &'static str { + match self { + ClusterEvent::PeerAlive => "A peer is alive and reachable", + ClusterEvent::PeerDiscovered => "A new peer was discovered", + ClusterEvent::PeerOffline => "A peer is offline", + ClusterEvent::PeerSuspected => "A peer is suspected to be offline", + ClusterEvent::PeerSuspectedIsAlive => "A suspected peer is actually alive", + ClusterEvent::PeerBackOnline => "A peer came back online", + ClusterEvent::PeerLeaving => "A peer is leaving the cluster", + ClusterEvent::PeerHasConfigChanges => "A peer has configuration changes", + ClusterEvent::PeerHasListChanges => "A peer has list changes", + ClusterEvent::OneOrMorePeersOffline => "One or more peers are offline", + ClusterEvent::EmptyPacket => "Received an empty gossip packet", + ClusterEvent::InvalidPacket => "Received an invalid gossip packet", + ClusterEvent::DecryptionError => "Failed to decrypt a gossip packet", + ClusterEvent::Error => "An error occurred in the cluster", + } + } } impl HousekeeperEvent { @@ -100,6 +177,17 @@ impl HousekeeperEvent { HousekeeperEvent::PurgeStore => "Purging store", } } + + pub fn explain(&self) -> &'static str { + match self { + HousekeeperEvent::Start => "The housekeeper process has started", + HousekeeperEvent::Stop => "The housekeeper process has stopped", + HousekeeperEvent::Schedule => "A housekeeper task has been scheduled", + HousekeeperEvent::PurgeAccounts => "Purging accounts", + HousekeeperEvent::PurgeSessions => "Purging sessions", + HousekeeperEvent::PurgeStore => "Purging store", + } + } } impl FtsIndexEvent { @@ -112,6 +200,16 @@ impl FtsIndexEvent { FtsIndexEvent::MetadataNotFound => "Metadata not found for full-text indexing", } } + + pub fn explain(&self) -> &'static str { + match self { + FtsIndexEvent::Index => "The full-text search index has been updated", + FtsIndexEvent::Locked => "The full-text search index is locked", + FtsIndexEvent::LockBusy => "The full-text search index lock is busy", + FtsIndexEvent::BlobNotFound => "The blob was not found for full-text indexing", + FtsIndexEvent::MetadataNotFound => "The metadata was not found for full-text indexing", + } + } } impl ImapEvent { @@ -155,6 +253,47 @@ impl ImapEvent { ImapEvent::ConnectionEnd => "IMAP connection ended", } } + + pub fn explain(&self) -> &'static str { + match self { + ImapEvent::GetAcl => "Client requested mailbox ACL", + ImapEvent::SetAcl => "Client set mailbox ACL", + ImapEvent::MyRights => "Client requested mailbox rights", + ImapEvent::ListRights => "Client requested mailbox rights list", + ImapEvent::Append => "Client appended a message to a mailbox", + ImapEvent::Capabilities => "Client requested server capabilities", + ImapEvent::Id => "Client sent an ID command", + ImapEvent::Close => "Client closed a mailbox", + ImapEvent::Copy => "Client copied messages between mailboxes", + ImapEvent::Move => "Client moved messages between mailboxes", + ImapEvent::CreateMailbox => "Client created a mailbox", + ImapEvent::DeleteMailbox => "Client deleted a mailbox", + ImapEvent::RenameMailbox => "Client renamed a mailbox", + ImapEvent::Enable => "Client enabled an extension", + ImapEvent::Expunge => "Client expunged messages", + ImapEvent::Fetch => "Client fetched messages", + ImapEvent::IdleStart => "Client started IDLE", + ImapEvent::IdleStop => "Client stopped IDLE", + ImapEvent::List => "Client listed mailboxes", + ImapEvent::Lsub => "Client listed subscribed mailboxes", + ImapEvent::Logout => "Client logged out", + ImapEvent::Namespace => "Client requested namespace", + ImapEvent::Noop => "Client sent a NOOP command", + ImapEvent::Search => "Client searched for messages", + ImapEvent::Sort => "Client sorted messages", + ImapEvent::Select => "Client selected a mailbox", + ImapEvent::Status => "Client requested mailbox status", + ImapEvent::Store => "Client stored flags", + ImapEvent::Subscribe => "Client subscribed to a mailbox", + ImapEvent::Unsubscribe => "Client unsubscribed from a mailbox", + ImapEvent::Thread => "Client requested message threads", + ImapEvent::Error => "An error occurred during an IMAP command", + ImapEvent::RawInput => "Raw IMAP input received", + ImapEvent::RawOutput => "Raw IMAP output sent", + ImapEvent::ConnectionStart => "IMAP connection started", + ImapEvent::ConnectionEnd => "IMAP connection ended", + } + } } impl Pop3Event { @@ -180,6 +319,29 @@ impl Pop3Event { Pop3Event::ConnectionEnd => "POP3 connection ended", } } + + pub fn explain(&self) -> &'static str { + match self { + Pop3Event::Delete => "Client deleted a message", + Pop3Event::Reset => "Client reset the session", + Pop3Event::Quit => "Client quit the session", + Pop3Event::Fetch => "Client fetched a message", + Pop3Event::List => "Client listed messages", + Pop3Event::ListMessage => "Client listed a specific message", + Pop3Event::Uidl => "Client requested unique identifiers", + Pop3Event::UidlMessage => "Client requested a specific unique identifier", + Pop3Event::Stat => "Client requested mailbox status", + Pop3Event::Noop => "Client sent a NOOP command", + Pop3Event::Capabilities => "Client requested server capabilities", + Pop3Event::StartTls => "Client requested TLS", + Pop3Event::Utf8 => "Client requested UTF-8 support", + Pop3Event::Error => "An error occurred during a POP3 command", + Pop3Event::RawInput => "Raw POP3 input received", + Pop3Event::RawOutput => "Raw POP3 output sent", + Pop3Event::ConnectionStart => "POP3 connection started", + Pop3Event::ConnectionEnd => "POP3 connection ended", + } + } } impl ManageSieveEvent { @@ -206,6 +368,30 @@ impl ManageSieveEvent { ManageSieveEvent::ConnectionEnd => "ManageSieve connection ended", } } + + pub fn explain(&self) -> &'static str { + match self { + ManageSieveEvent::CreateScript => "Client created a script", + ManageSieveEvent::UpdateScript => "Client updated a script", + ManageSieveEvent::GetScript => "Client fetched a script", + ManageSieveEvent::DeleteScript => "Client deleted a script", + ManageSieveEvent::RenameScript => "Client renamed a script", + ManageSieveEvent::CheckScript => "Client checked a script", + ManageSieveEvent::HaveSpace => "Client checked for space", + ManageSieveEvent::ListScripts => "Client listed scripts", + ManageSieveEvent::SetActive => "Client set an active script", + ManageSieveEvent::Capabilities => "Client requested server capabilities", + ManageSieveEvent::StartTls => "Client requested TLS", + ManageSieveEvent::Unauthenticate => "Client unauthenticated", + ManageSieveEvent::Logout => "Client logged out", + ManageSieveEvent::Noop => "Client sent a NOOP command", + ManageSieveEvent::Error => "An error occurred during a ManageSieve command", + ManageSieveEvent::RawInput => "Raw ManageSieve input received", + ManageSieveEvent::RawOutput => "Raw ManageSieve output sent", + ManageSieveEvent::ConnectionStart => "ManageSieve connection started", + ManageSieveEvent::ConnectionEnd => "ManageSieve connection ended", + } + } } impl SmtpEvent { @@ -241,7 +427,7 @@ impl SmtpEvent { SmtpEvent::DidNotSayEhlo => "Client did not say EHLO", SmtpEvent::EhloExpected => "EHLO command expected", SmtpEvent::LhloExpected => "LHLO command expected", - SmtpEvent::MailFromUnauthenticated => "MAIL FROM unauthenticated", + SmtpEvent::MailFromUnauthenticated => "MAIL FROM without authentication", SmtpEvent::MailFromUnauthorized => "MAIL FROM unauthorized", SmtpEvent::MailFromRewritten => "MAIL FROM address rewritten", SmtpEvent::MailFromMissing => "MAIL FROM address missing", @@ -295,6 +481,127 @@ impl SmtpEvent { SmtpEvent::ConnectionEnd => "SMTP connection ended", } } + + pub fn explain(&self) -> &'static str { + match self { + SmtpEvent::Error => "An error occurred during an SMTP command", + SmtpEvent::RemoteIdNotFound => { + "The remote server ID was not found in the configuration" + } + SmtpEvent::ConcurrencyLimitExceeded => "The concurrency limit was exceeded", + SmtpEvent::TransferLimitExceeded => { + "The remote host transferred more data than allowed" + } + SmtpEvent::RateLimitExceeded => "The rate limit was exceeded", + SmtpEvent::TimeLimitExceeded => "The remote host kept the SMTP session open too long", + SmtpEvent::MissingAuthDirectory => "The auth directory was missing", + SmtpEvent::MessageParseFailed => "Failed to parse the message", + SmtpEvent::MessageTooLarge => "The message was rejected because it was too large", + SmtpEvent::LoopDetected => { + "A mail loop was detected, the message contains too many Received headers" + } + SmtpEvent::PipeSuccess => "The pipe command succeeded", + SmtpEvent::PipeError => "The pipe command failed", + SmtpEvent::DkimPass => "Successful DKIM verification", + SmtpEvent::DkimFail => "Failed to verify DKIM signature", + SmtpEvent::ArcPass => "Successful ARC verification", + SmtpEvent::ArcFail => "Failed to verify ARC signature", + SmtpEvent::SpfEhloPass => "EHLO identity passed SPF check", + SmtpEvent::SpfEhloFail => "EHLO identity failed SPF check", + SmtpEvent::SpfFromPass => "MAIL FROM identity passed SPF check", + SmtpEvent::SpfFromFail => "MAIL FROM identity failed SPF check", + SmtpEvent::DmarcPass => "Successful DMARC verification", + SmtpEvent::DmarcFail => "Failed to verify DMARC policy", + SmtpEvent::IprevPass => "Reverse IP check passed", + SmtpEvent::IprevFail => "Reverse IP check failed", + SmtpEvent::TooManyMessages => { + "The remote server exceeded the number of messages allowed per session" + } + SmtpEvent::Ehlo => "The remote server sent an EHLO command", + SmtpEvent::InvalidEhlo => "The remote server sent an invalid EHLO command", + SmtpEvent::DidNotSayEhlo => "The remote server did not send EHLO command", + SmtpEvent::EhloExpected => { + "The remote server sent a LHLO command while EHLO was expected" + } + SmtpEvent::LhloExpected => { + "The remote server sent an EHLO command while LHLO was expected" + } + SmtpEvent::MailFromUnauthenticated => { + "The remote client did not authenticate before sending MAIL FROM" + } + SmtpEvent::MailFromUnauthorized => { + "The remote client is not authorized to send mail from the given address" + } + SmtpEvent::MailFromRewritten => "The envelope sender address was rewritten", + SmtpEvent::MailFromMissing => { + "The remote client issued an RCPT TO command before MAIL FROM" + } + SmtpEvent::MailFrom => "The remote client sent a MAIL FROM command", + SmtpEvent::MultipleMailFrom => "The remote client already sent a MAIL FROM command", + SmtpEvent::MailboxDoesNotExist => "The mailbox does not exist on the server", + SmtpEvent::RelayNotAllowed => "The server does not allow relaying", + SmtpEvent::RcptTo => "The remote client sent an RCPT TO command", + SmtpEvent::RcptToDuplicate => { + "The remote client already sent an RCPT TO command for this recipient" + } + SmtpEvent::RcptToRewritten => "The envelope recipient address was rewritten", + SmtpEvent::RcptToMissing => "The remote client issued a DATA command before RCPT TO", + SmtpEvent::TooManyRecipients => { + "The remote client exceeded the number of recipients allowed" + } + SmtpEvent::TooManyInvalidRcpt => { + "The remote client exceeded the number of invalid RCPT TO commands allowed" + } + SmtpEvent::RawInput => "Raw SMTP input received", + SmtpEvent::RawOutput => "Raw SMTP output sent", + SmtpEvent::MissingLocalHostname => "The local hostname is missing in the configuration", + SmtpEvent::Vrfy => "The remote client sent a VRFY command", + SmtpEvent::VrfyNotFound => { + "The remote client sent a VRFY command for an address that was not found" + } + SmtpEvent::VrfyDisabled => "The VRFY command is disabled", + SmtpEvent::Expn => "The remote client sent an EXPN command", + SmtpEvent::ExpnNotFound => { + "The remote client sent an EXPN command for an address that was not found" + } + SmtpEvent::ExpnDisabled => "The EXPN command is disabled", + SmtpEvent::RequireTlsDisabled => "The REQUIRETLS extension is disabled", + SmtpEvent::DeliverByDisabled => "The DELIVERBY extension is disabled", + SmtpEvent::DeliverByInvalid => "The DELIVERBY parameter is invalid", + SmtpEvent::FutureReleaseDisabled => "The FUTURE RELEASE extension is disabled", + SmtpEvent::FutureReleaseInvalid => "The FUTURE RELEASE parameter is invalid", + SmtpEvent::MtPriorityDisabled => "The MT-PRIORITY extension is disabled", + SmtpEvent::MtPriorityInvalid => "The MT-PRIORITY parameter is invalid", + SmtpEvent::DsnDisabled => "The DSN extension is disabled", + SmtpEvent::AuthNotAllowed => "Authentication is not allowed on this listener", + SmtpEvent::AuthMechanismNotSupported => { + "The requested authentication mechanism is not supported" + } + SmtpEvent::AuthExchangeTooLong => "The authentication exchange was too long", + SmtpEvent::AlreadyAuthenticated => "The client is already authenticated", + SmtpEvent::Noop => "The remote client sent a NOOP command", + SmtpEvent::StartTls => "The remote client requested a TLS connection", + SmtpEvent::StartTlsUnavailable => { + "The remote client requested a TLS connection but it is not available" + } + SmtpEvent::Rset => "The remote client sent a RSET command", + SmtpEvent::Quit => "The remote client sent a QUIT command", + SmtpEvent::Help => "The remote client sent a HELP command", + SmtpEvent::CommandNotImplemented => { + "The server does not implement the requested command" + } + SmtpEvent::InvalidCommand => "The remote client sent an invalid command", + SmtpEvent::InvalidSenderAddress => "The specified sender address is invalid", + SmtpEvent::InvalidRecipientAddress => "The specified recipient address is invalid", + SmtpEvent::InvalidParameter => "The command contained an invalid parameter", + SmtpEvent::UnsupportedParameter => "The command contained an unsupported parameter", + SmtpEvent::SyntaxError => "The command contained a syntax error", + SmtpEvent::RequestTooLarge => "The request was too large", + SmtpEvent::ConnectionStart => "A new SMTP connection was started", + SmtpEvent::ConnectionEnd => "The SMTP connection was ended", + SmtpEvent::StartTlsAlready => "TLS is already active", + } + } } impl DeliveryEvent { @@ -340,6 +647,63 @@ impl DeliveryEvent { DeliveryEvent::RawOutput => "Raw SMTP output sent", } } + + pub fn explain(&self) -> &'static str { + match self { + DeliveryEvent::AttemptStart => "A new delivery attempt for the message has started", + DeliveryEvent::AttemptEnd => "The delivery attempt has ended", + DeliveryEvent::Completed => "Delivery was completed for all recipients", + DeliveryEvent::Failed => "Message delivery failed due to a temporary error", + DeliveryEvent::DomainDeliveryStart => "A new delivery attempt for a domain has started", + DeliveryEvent::MxLookup => "Looking up MX records for the domain", + DeliveryEvent::MxLookupFailed => "Failed to look up MX records for the domain", + DeliveryEvent::IpLookup => "Looking up IP address for the domain", + DeliveryEvent::IpLookupFailed => "Failed to look up IP address for the domain", + DeliveryEvent::NullMx => "The domain has a null MX record, delivery is impossible", + DeliveryEvent::Connect => "Connecting to the remote server", + DeliveryEvent::ConnectError => "Error connecting to the remote server", + DeliveryEvent::MissingOutboundHostname => { + "The outbound hostname is missing in the configuration" + } + DeliveryEvent::GreetingFailed => { + "Failed to read the SMTP greeting from the remote server" + } + DeliveryEvent::Ehlo => "The EHLO command was sent to the remote server", + DeliveryEvent::EhloRejected => "The remote server rejected the EHLO command", + DeliveryEvent::Auth => "Authenticating with the remote server", + DeliveryEvent::AuthFailed => "Authentication with the remote server failed", + DeliveryEvent::MailFrom => "The MAIL FROM command was sent to the remote server", + DeliveryEvent::MailFromRejected => "The remote server rejected the MAIL FROM command", + DeliveryEvent::Delivered => "The message was delivered to the recipient", + DeliveryEvent::RcptTo => "The RCPT TO command was sent to the remote server", + DeliveryEvent::RcptToRejected => "The remote server rejected the RCPT TO command", + DeliveryEvent::RcptToFailed => { + "Failed to send the RCPT TO command to the remote server" + } + DeliveryEvent::MessageRejected => "The remote server rejected the message", + DeliveryEvent::StartTls => "Requesting a TLS connection with the remote server", + DeliveryEvent::StartTlsUnavailable => "The remote server does not support STARTTLS", + DeliveryEvent::StartTlsError => "It was not possible to establish a TLS connection", + DeliveryEvent::StartTlsDisabled => { + "STARTTLS has been disabled in the configuration for this host" + } + DeliveryEvent::ImplicitTlsError => "Error starting implicit TLS", + DeliveryEvent::ConcurrencyLimitExceeded => { + "The concurrency limit was exceeded for the remote host" + } + DeliveryEvent::RateLimitExceeded => "The rate limit was exceeded for the remote host", + DeliveryEvent::DoubleBounce => "The message was discarded after a double bounce", + DeliveryEvent::DsnSuccess => "A success delivery status notification was created", + DeliveryEvent::DsnTempFail => { + "A temporary failure delivery status notification was created" + } + DeliveryEvent::DsnPermFail => { + "A permanent failure delivery status notification was created" + } + DeliveryEvent::RawInput => "Raw SMTP input received", + DeliveryEvent::RawOutput => "Raw SMTP output sent", + } + } } impl QueueEvent { @@ -353,12 +717,31 @@ impl QueueEvent { QueueEvent::ConcurrencyLimitExceeded => "Concurrency limit exceeded", QueueEvent::QuotaExceeded => "Quota exceeded", QueueEvent::QueueMessage => "Queued message for delivery", - QueueEvent::QueueMessageSubmission => "Queued message submissions for delivery", + QueueEvent::QueueMessageAuthenticated => "Queued message submission for delivery", QueueEvent::QueueReport => "Queued report for delivery", QueueEvent::QueueDsn => "Queued DSN for delivery", QueueEvent::QueueAutogenerated => "Queued autogenerated message for delivery", } } + + pub fn explain(&self) -> &'static str { + match self { + QueueEvent::Rescheduled => "The message was rescheduled for delivery", + QueueEvent::LockBusy => "The queue lock is busy", + QueueEvent::Locked => "The queue is locked", + QueueEvent::BlobNotFound => "The message blob was not found", + QueueEvent::RateLimitExceeded => "The queue rate limit was exceeded", + QueueEvent::ConcurrencyLimitExceeded => "The queue concurrency limit was exceeded", + QueueEvent::QuotaExceeded => "The queue quota was exceeded", + QueueEvent::QueueMessage => "A new message was queued for delivery", + QueueEvent::QueueMessageAuthenticated => { + "A new message was queued for delivery from an authenticated client" + } + QueueEvent::QueueReport => "A new report was queued for delivery", + QueueEvent::QueueDsn => "A delivery status notification was queued for delivery", + QueueEvent::QueueAutogenerated => "A system generated message was queued for delivery", + } + } } impl IncomingReportEvent { @@ -381,6 +764,34 @@ impl IncomingReportEvent { IncomingReportEvent::DecompressError => "Error decompressing report", } } + + pub fn explain(&self) -> &'static str { + match self { + IncomingReportEvent::DmarcReport => "A DMARC report has been received", + IncomingReportEvent::DmarcReportWithWarnings => { + "A DMARC report with warnings has been received" + } + IncomingReportEvent::TlsReport => "A TLS report has been received", + IncomingReportEvent::TlsReportWithWarnings => { + "A TLS report with warnings has been received" + } + IncomingReportEvent::AbuseReport => "An abuse report has been received", + IncomingReportEvent::AuthFailureReport => { + "An authentication failure report has been received" + } + IncomingReportEvent::FraudReport => "A fraud report has been received", + IncomingReportEvent::NotSpamReport => "A not spam report has been received", + IncomingReportEvent::VirusReport => "A virus report has been received", + IncomingReportEvent::OtherReport => "An unknown type of report has been received", + IncomingReportEvent::MessageParseFailed => { + "Failed to parse the incoming report message" + } + IncomingReportEvent::DmarcParseFailed => "Failed to parse the DMARC report", + IncomingReportEvent::TlsRpcParseFailed => "Failed to parse the TLS RPC report", + IncomingReportEvent::ArfParseFailed => "Failed to parse the ARF report", + IncomingReportEvent::DecompressError => "Error decompressing the report", + } + } } impl OutgoingReportEvent { @@ -407,6 +818,32 @@ impl OutgoingReportEvent { OutgoingReportEvent::Locked => "Report is locked", } } + + pub fn explain(&self) -> &'static str { + match self { + OutgoingReportEvent::SpfReport => "An SPF report has been sent", + OutgoingReportEvent::SpfRateLimited => "The SPF report was rate limited", + OutgoingReportEvent::DkimReport => "A DKIM report has been sent", + OutgoingReportEvent::DkimRateLimited => "The DKIM report was rate limited", + OutgoingReportEvent::DmarcReport => "A DMARC report has been sent", + OutgoingReportEvent::DmarcRateLimited => "The DMARC report was rate limited", + OutgoingReportEvent::DmarcAggregateReport => "A DMARC aggregate report has been sent", + OutgoingReportEvent::TlsAggregate => "A TLS aggregate report has been sent", + OutgoingReportEvent::HttpSubmission => "The report was submitted via HTTP", + OutgoingReportEvent::UnauthorizedReportingAddress => { + "The reporting address is not authorized to send reports" + } + OutgoingReportEvent::ReportingAddressValidationError => { + "Error validating the reporting address" + } + OutgoingReportEvent::NotFound => "The report was not found", + OutgoingReportEvent::SubmissionError => "Error submitting the report", + OutgoingReportEvent::NoRecipientsFound => "No recipients found for the report", + OutgoingReportEvent::LockBusy => "The report lock is busy", + OutgoingReportEvent::LockDeleted => "The report lock was deleted", + OutgoingReportEvent::Locked => "The report is locked", + } + } } impl MtaStsEvent { @@ -420,6 +857,17 @@ impl MtaStsEvent { MtaStsEvent::InvalidPolicy => "Invalid MTA-STS policy", } } + + pub fn explain(&self) -> &'static str { + match self { + MtaStsEvent::Authorized => "The host is authorized by the MTA-STS policy", + MtaStsEvent::NotAuthorized => "The host is not authorized by the MTA-STS policy", + MtaStsEvent::PolicyFetch => "The MTA-STS policy has been fetched", + MtaStsEvent::PolicyNotFound => "An MTA-STS policy was not found", + MtaStsEvent::PolicyFetchError => "An error occurred while fetching the MTA-STS policy", + MtaStsEvent::InvalidPolicy => "The MTA-STS policy is invalid", + } + } } impl TlsRptEvent { @@ -429,6 +877,13 @@ impl TlsRptEvent { TlsRptEvent::RecordFetchError => "Error fetching TLS-RPT record", } } + + pub fn explain(&self) -> &'static str { + match self { + TlsRptEvent::RecordFetch => "The TLS-RPT record has been fetched", + TlsRptEvent::RecordFetchError => "An error occurred while fetching the TLS-RPT record", + } + } } impl DaneEvent { @@ -446,6 +901,21 @@ impl DaneEvent { DaneEvent::TlsaRecordInvalid => "Invalid TLSA record", } } + + pub fn explain(&self) -> &'static str { + match self { + DaneEvent::AuthenticationSuccess => "Successful DANE authentication", + DaneEvent::AuthenticationFailure => "Failed DANE authentication", + DaneEvent::NoCertificatesFound => "No certificates were found for DANE", + DaneEvent::CertificateParseError => "An error occurred while parsing the certificate", + DaneEvent::TlsaRecordMatch => "A TLSA record match was found", + DaneEvent::TlsaRecordFetch => "The TLSA record has been fetched", + DaneEvent::TlsaRecordFetchError => "An error occurred while fetching the TLSA record", + DaneEvent::TlsaRecordNotFound => "The TLSA record was not found", + DaneEvent::TlsaRecordNotDnssecSigned => "The TLSA record is not DNSSEC signed", + DaneEvent::TlsaRecordInvalid => "The TLSA record is invalid", + } + } } impl MilterEvent { @@ -470,6 +940,30 @@ impl MilterEvent { MilterEvent::ParseError => "Milter parse error", } } + + pub fn explain(&self) -> &'static str { + match self { + MilterEvent::Read => "Reading from the Milter", + MilterEvent::Write => "Writing to the Milter", + MilterEvent::ActionAccept => "The Milter requested to accept the message", + MilterEvent::ActionDiscard => "The Milter requested to discard the message", + MilterEvent::ActionReject => "The Milter requested to reject the message", + MilterEvent::ActionTempFail => "The Milter requested to temporarily fail the message", + MilterEvent::ActionReplyCode => "The Milter requested a reply code", + MilterEvent::ActionConnectionFailure => "The Milter requested a connection failure", + MilterEvent::ActionShutdown => "The Milter requested a shutdown", + MilterEvent::IoError => "An I/O error occurred with the Milter", + MilterEvent::FrameTooLarge => "The Milter frame was too large", + MilterEvent::FrameInvalid => "The Milter frame was invalid", + MilterEvent::UnexpectedResponse => { + "An unexpected response was received from the Milter" + } + MilterEvent::Timeout => "A timeout occurred with the Milter", + MilterEvent::TlsInvalidName => "The Milter TLS name is invalid", + MilterEvent::Disconnected => "The Milter disconnected", + MilterEvent::ParseError => "An error occurred while parsing the Milter response", + } + } } impl MtaHookEvent { @@ -482,6 +976,16 @@ impl MtaHookEvent { MtaHookEvent::Error => "MTA hook error", } } + + pub fn explain(&self) -> &'static str { + match self { + MtaHookEvent::ActionAccept => "The MTA hook requested to accept the message", + MtaHookEvent::ActionDiscard => "The MTA hook requested to discard the message", + MtaHookEvent::ActionReject => "The MTA hook requested to reject the message", + MtaHookEvent::ActionQuarantine => "The MTA hook requested to quarantine the message", + MtaHookEvent::Error => "An error occurred with the MTA hook", + } + } } impl PushSubscriptionEvent { @@ -492,6 +996,14 @@ impl PushSubscriptionEvent { PushSubscriptionEvent::NotFound => "Push subscription not found", } } + + pub fn explain(&self) -> &'static str { + match self { + PushSubscriptionEvent::Success => "The push subscription was successful", + PushSubscriptionEvent::Error => "An error occurred with the push subscription", + PushSubscriptionEvent::NotFound => "The push subscription was not found", + } + } } impl SpamEvent { @@ -507,6 +1019,21 @@ impl SpamEvent { SpamEvent::NotEnoughTrainingData => "Not enough training data for spam filter", } } + + pub fn explain(&self) -> &'static str { + match self { + SpamEvent::PyzorError => "An error occurred with Pyzor", + SpamEvent::ListUpdated => "The spam list has been updated", + SpamEvent::Train => "The spam filter is being trained with the message", + SpamEvent::TrainBalance => "The spam filter training data is being balanced", + SpamEvent::TrainError => "An error occurred while training the spam filter", + SpamEvent::Classify => "The message is being classified for spam", + SpamEvent::ClassifyError => "An error occurred while classifying the message for spam", + SpamEvent::NotEnoughTrainingData => { + "There is not enough training data for the spam filter" + } + } + } } impl SieveEvent { @@ -526,6 +1053,25 @@ impl SieveEvent { SieveEvent::QuotaExceeded => "Sieve quota exceeded", } } + + pub fn explain(&self) -> &'static str { + match self { + SieveEvent::ActionAccept => "The Sieve script requested to accept the message", + SieveEvent::ActionAcceptReplace => { + "The Sieve script requested to accept the message and replace its contents" + } + SieveEvent::ActionDiscard => "The Sieve script requested to discard the message", + SieveEvent::ActionReject => "The Sieve script requested to reject the message", + SieveEvent::SendMessage => "The Sieve script is sending a message", + SieveEvent::MessageTooLarge => "The Sieve message is too large", + SieveEvent::ScriptNotFound => "The Sieve script was not found", + SieveEvent::ListNotFound => "The Sieve list was not found", + SieveEvent::RuntimeError => "A runtime error occurred with the Sieve script", + SieveEvent::UnexpectedError => "An unexpected error occurred with the Sieve script", + SieveEvent::NotSupported => "The Sieve action is not supported", + SieveEvent::QuotaExceeded => "The Sieve quota was exceeded", + } + } } impl TlsEvent { @@ -539,6 +1085,17 @@ impl TlsEvent { TlsEvent::MultipleCertificatesAvailable => "Multiple TLS certificates available", } } + + pub fn explain(&self) -> &'static str { + match self { + TlsEvent::Handshake => "Successful TLS handshake", + TlsEvent::HandshakeError => "An error occurred during the TLS handshake", + TlsEvent::NotConfigured => "TLS is not configured", + TlsEvent::CertificateNotFound => "The TLS certificate was not found", + TlsEvent::NoCertificatesAvailable => "No TLS certificates are available", + TlsEvent::MultipleCertificatesAvailable => "Multiple TLS certificates are available", + } + } } impl NetworkEvent { @@ -560,6 +1117,25 @@ impl NetworkEvent { NetworkEvent::DropBlocked => "Dropped connection from blocked IP address", } } + + pub fn explain(&self) -> &'static str { + match self { + NetworkEvent::ListenStart => "The network listener has started", + NetworkEvent::ListenStop => "The network listener has stopped", + NetworkEvent::ListenError => "An error occurred with the network listener", + NetworkEvent::BindError => "An error occurred while binding the network listener", + NetworkEvent::ReadError => "An error occurred while reading from the network", + NetworkEvent::WriteError => "An error occurred while writing to the network", + NetworkEvent::FlushError => "An error occurred while flushing the network", + NetworkEvent::AcceptError => "An error occurred while accepting a network connection", + NetworkEvent::SplitError => "An error occurred while splitting the network connection", + NetworkEvent::Timeout => "A network timeout occurred", + NetworkEvent::Closed => "The network connection was closed", + NetworkEvent::ProxyError => "An error occurred with the proxy protocol", + NetworkEvent::SetOptError => "An error occurred while setting network options", + NetworkEvent::DropBlocked => "The connection was dropped from a blocked IP address", + } + } } impl ServerEvent { @@ -577,6 +1153,16 @@ impl ServerEvent { ServerEvent::Licensing => "Server licensing event", } } + + pub fn explain(&self) -> &'static str { + match self { + ServerEvent::Startup => "Stalwart Mail Server has started", + ServerEvent::Shutdown => "Stalwart Mail Server is shutting down", + ServerEvent::StartupError => "An error occurred while starting the server", + ServerEvent::ThreadError => "An error occurred with a server thread", + ServerEvent::Licensing => "A licensing event occurred", + } + } } impl TelemetryEvent { @@ -590,6 +1176,23 @@ impl TelemetryEvent { TelemetryEvent::PrometheusExporterError => "Prometheus exporter error", } } + + pub fn explain(&self) -> &'static str { + match self { + TelemetryEvent::LogError => "An error occurred with the log collector", + TelemetryEvent::WebhookError => "An error occurred with the webhook collector", + TelemetryEvent::JournalError => "An error occurred with the journal collector", + TelemetryEvent::OtelExporterError => { + "An error occurred with the OpenTelemetry exporter" + } + TelemetryEvent::OtelMetricsExporterError => { + "An error occurred with the OpenTelemetry metrics exporter" + } + TelemetryEvent::PrometheusExporterError => { + "An error occurred with the Prometheus exporter" + } + } + } } impl AcmeEvent { @@ -624,6 +1227,38 @@ impl AcmeEvent { AcmeEvent::Error => "ACME error", } } + + pub fn explain(&self) -> &'static str { + match self { + AcmeEvent::AuthStart => "ACME authentication has started", + AcmeEvent::AuthPending => "ACME authentication is pending", + AcmeEvent::AuthValid => "ACME authentication is valid", + AcmeEvent::AuthCompleted => "ACME authentication has completed", + AcmeEvent::AuthError => "An error occurred with ACME authentication", + AcmeEvent::AuthTooManyAttempts => "Too many ACME authentication attempts", + AcmeEvent::ProcessCert => "Processing the ACME certificate", + AcmeEvent::OrderStart => "ACME order has started", + AcmeEvent::OrderProcessing => "ACME order is processing", + AcmeEvent::OrderCompleted => "ACME order has completed", + AcmeEvent::OrderReady => "ACME order is ready", + AcmeEvent::OrderValid => "ACME order is valid", + AcmeEvent::OrderInvalid => "ACME order is invalid", + AcmeEvent::RenewBackoff => "ACME renew backoff", + AcmeEvent::DnsRecordCreated => "ACME DNS record has been created", + AcmeEvent::DnsRecordCreationFailed => "Failed to create ACME DNS record", + AcmeEvent::DnsRecordDeletionFailed => "Failed to delete ACME DNS record", + AcmeEvent::DnsRecordNotPropagated => "ACME DNS record has not propagated", + AcmeEvent::DnsRecordLookupFailed => "Failed to look up ACME DNS record", + AcmeEvent::DnsRecordPropagated => "ACME DNS record has propagated", + AcmeEvent::DnsRecordPropagationTimeout => "ACME DNS record propagation timeout", + AcmeEvent::ClientSuppliedSni => "ACME client supplied SNI", + AcmeEvent::ClientMissingSni => "ACME client missing SNI", + AcmeEvent::TlsAlpnReceived => "ACME TLS ALPN received", + AcmeEvent::TlsAlpnError => "ACME TLS ALPN error", + AcmeEvent::TokenNotFound => "ACME token not found", + AcmeEvent::Error => "An error occurred with ACME", + } + } } impl PurgeEvent { @@ -638,6 +1273,18 @@ impl PurgeEvent { PurgeEvent::TombstoneCleanup => "Tombstone cleanup executed", } } + + pub fn explain(&self) -> &'static str { + match self { + PurgeEvent::Started => "The purge has started", + PurgeEvent::Finished => "The purge has finished", + PurgeEvent::Running => "The purge is running", + PurgeEvent::Error => "An error occurred with the purge", + PurgeEvent::PurgeActive => "An active purge is in progress", + PurgeEvent::AutoExpunge => "Auto-expunge has been executed", + PurgeEvent::TombstoneCleanup => "Tombstone cleanup has been executed", + } + } } impl EvalEvent { @@ -649,6 +1296,17 @@ impl EvalEvent { EvalEvent::StoreNotFound => "Store not found while evaluating expression", } } + + pub fn explain(&self) -> &'static str { + match self { + EvalEvent::Result => "The expression evaluation has a result", + EvalEvent::Error => "An error occurred while evaluating the expression", + EvalEvent::DirectoryNotFound => { + "The directory was not found while evaluating the expression" + } + EvalEvent::StoreNotFound => "The store was not found while evaluating the expression", + } + } } impl ConfigEvent { @@ -669,6 +1327,24 @@ impl ConfigEvent { ConfigEvent::AlreadyUpToDate => "Configuration already up to date", } } + + pub fn explain(&self) -> &'static str { + match self { + ConfigEvent::ParseError => "An error occurred while parsing the configuration", + ConfigEvent::BuildError => "An error occurred while building the configuration", + ConfigEvent::MacroError => "An error occurred with a configuration macro", + ConfigEvent::WriteError => "An error occurred while writing the configuration", + ConfigEvent::FetchError => "An error occurred while fetching the configuration", + ConfigEvent::DefaultApplied => "The default configuration has been applied", + ConfigEvent::MissingSetting => "A configuration setting is missing", + ConfigEvent::UnusedSetting => "A configuration setting is unused", + ConfigEvent::ParseWarning => "A warning occurred while parsing the configuration", + ConfigEvent::BuildWarning => "A warning occurred while building the configuration", + ConfigEvent::ImportExternal => "An external configuration is being imported", + ConfigEvent::ExternalKeyIgnored => "An external configuration key is ignored", + ConfigEvent::AlreadyUpToDate => "The configuration is already up to date", + } + } } impl ArcEvent { @@ -682,6 +1358,17 @@ impl ArcEvent { ArcEvent::SealerNotFound => "ARC sealer not found", } } + + pub fn explain(&self) -> &'static str { + match self { + ArcEvent::ChainTooLong => "The ARC chain is too long", + ArcEvent::InvalidInstance => "The ARC instance is invalid", + ArcEvent::InvalidCv => "The ARC CV is invalid", + ArcEvent::HasHeaderTag => "The ARC has a header tag", + ArcEvent::BrokenChain => "The ARC chain is broken", + ArcEvent::SealerNotFound => "The ARC sealer was not found", + } + } } impl DkimEvent { @@ -707,6 +1394,29 @@ impl DkimEvent { DkimEvent::SignerNotFound => "DKIM signer not found", } } + + pub fn explain(&self) -> &'static str { + match self { + DkimEvent::Pass => "DKIM verification has passed", + DkimEvent::Neutral => "DKIM verification is neutral", + DkimEvent::Fail => "DKIM verification has failed", + DkimEvent::PermError => "A permanent error occurred with DKIM", + DkimEvent::TempError => "A temporary error occurred with DKIM", + DkimEvent::None => "No DKIM signature was found", + DkimEvent::UnsupportedVersion => "The DKIM version is unsupported", + DkimEvent::UnsupportedAlgorithm => "The DKIM algorithm is unsupported", + DkimEvent::UnsupportedCanonicalization => "The DKIM canonicalization is unsupported", + DkimEvent::UnsupportedKeyType => "The DKIM key type is unsupported", + DkimEvent::FailedBodyHashMatch => "The DKIM body hash does not match", + DkimEvent::FailedVerification => "The DKIM verification has failed", + DkimEvent::FailedAuidMatch => "The DKIM AUID does not match", + DkimEvent::RevokedPublicKey => "The DKIM public key has been revoked", + DkimEvent::IncompatibleAlgorithms => "The DKIM algorithms are incompatible", + DkimEvent::SignatureExpired => "The DKIM signature has expired", + DkimEvent::SignatureLength => "The DKIM signature length is incorrect", + DkimEvent::SignerNotFound => "The DKIM signer was not found", + } + } } impl SpfEvent { @@ -721,6 +1431,18 @@ impl SpfEvent { SpfEvent::None => "No SPF record", } } + + pub fn explain(&self) -> &'static str { + match self { + SpfEvent::Pass => "The SPF check has passed", + SpfEvent::Fail => "The SPF check has failed", + SpfEvent::SoftFail => "The SPF check has soft failed", + SpfEvent::Neutral => "The SPF result is neutral", + SpfEvent::TempError => "A temporary error occurred with SPF", + SpfEvent::PermError => "A permanent error occurred with SPF", + SpfEvent::None => "No SPF record was found", + } + } } impl DmarcEvent { @@ -733,6 +1455,16 @@ impl DmarcEvent { DmarcEvent::None => "No DMARC record", } } + + pub fn explain(&self) -> &'static str { + match self { + DmarcEvent::Pass => "The DMARC check has passed", + DmarcEvent::Fail => "The DMARC check has failed", + DmarcEvent::PermError => "A permanent error occurred with DMARC", + DmarcEvent::TempError => "A temporary error occurred with DMARC", + DmarcEvent::None => "No DMARC record was found", + } + } } impl IprevEvent { @@ -745,6 +1477,16 @@ impl IprevEvent { IprevEvent::None => "No IPREV record", } } + + pub fn explain(&self) -> &'static str { + match self { + IprevEvent::Pass => "The IPREV check has passed", + IprevEvent::Fail => "The IPREV check has failed", + IprevEvent::PermError => "A permanent error occurred with IPREV", + IprevEvent::TempError => "A temporary error occurred with IPREV", + IprevEvent::None => "No IPREV record was found", + } + } } impl MailAuthEvent { @@ -762,6 +1504,21 @@ impl MailAuthEvent { MailAuthEvent::PolicyNotAligned => "Policy not aligned", } } + + pub fn explain(&self) -> &'static str { + match self { + MailAuthEvent::ParseError => "An error occurred while parsing mail authentication", + MailAuthEvent::MissingParameters => "Mail authentication parameters are missing", + MailAuthEvent::NoHeadersFound => "No headers were found in the message", + MailAuthEvent::Crypto => "A crypto error occurred during mail authentication", + MailAuthEvent::Io => "An I/O error occurred during mail authentication", + MailAuthEvent::Base64 => "A base64 error occurred during mail authentication", + MailAuthEvent::DnsError => "A DNS error occurred", + MailAuthEvent::DnsRecordNotFound => "The DNS record was not found", + MailAuthEvent::DnsInvalidRecordType => "The DNS record type is invalid", + MailAuthEvent::PolicyNotAligned => "The policy is not aligned", + } + } } impl StoreEvent { @@ -798,6 +1555,40 @@ impl StoreEvent { StoreEvent::DataIterate => "Data store iteration operation", } } + + pub fn explain(&self) -> &'static str { + match self { + StoreEvent::AssertValueFailed => "Another process modified the record", + StoreEvent::FoundationdbError => "A FoundationDB error occurred", + StoreEvent::MysqlError => "A MySQL error occurred", + StoreEvent::PostgresqlError => "A PostgreSQL error occurred", + StoreEvent::RocksdbError => "A RocksDB error occurred", + StoreEvent::SqliteError => "An SQLite error occurred", + StoreEvent::LdapError => "An LDAP error occurred", + StoreEvent::ElasticsearchError => "An ElasticSearch error occurred", + StoreEvent::RedisError => "A Redis error occurred", + StoreEvent::S3Error => "An S3 error occurred", + StoreEvent::FilesystemError => "A filesystem error occurred", + StoreEvent::PoolError => "A connection pool error occurred", + StoreEvent::DataCorruption => "Data corruption was detected", + StoreEvent::DecompressError => "A decompression error occurred", + StoreEvent::DeserializeError => "A deserialization error occurred", + StoreEvent::NotFound => "The record was not found in the database", + StoreEvent::NotConfigured => "The store is not configured", + StoreEvent::NotSupported => "The operation is not supported by the store", + StoreEvent::UnexpectedError => "An unexpected store error occurred", + StoreEvent::CryptoError => "A store crypto error occurred", + StoreEvent::BlobMissingMarker => "The blob is missing a marker", + StoreEvent::SqlQuery => "An SQL query was executed", + StoreEvent::LdapQuery => "An LDAP query was executed", + StoreEvent::LdapBind => "An LDAP bind operation was executed", + StoreEvent::DataWrite => "A write batch operation was executed", + StoreEvent::BlobRead => "A blob read operation was executed", + StoreEvent::BlobWrite => "A blob write operation was executed", + StoreEvent::BlobDelete => "A blob delete operation was executed", + StoreEvent::DataIterate => "A data store iteration operation was executed", + } + } } impl MessageIngestEvent { @@ -811,6 +1602,17 @@ impl MessageIngestEvent { MessageIngestEvent::Error => "Message ingestion error", } } + + pub fn explain(&self) -> &'static str { + match self { + MessageIngestEvent::Ham => "The message has been ingested", + MessageIngestEvent::Spam => "A possible spam message has been ingested", + MessageIngestEvent::ImapAppend => "The message has been appended via IMAP", + MessageIngestEvent::JmapAppend => "The message has been appended via JMAP", + MessageIngestEvent::Duplicate => "The message is a duplicate and has been skipped", + MessageIngestEvent::Error => "An error occurred while ingesting the message", + } + } } impl JmapEvent { @@ -840,6 +1642,35 @@ impl JmapEvent { JmapEvent::WebsocketError => "JMAP WebSocket error", } } + + pub fn explain(&self) -> &'static str { + match self { + JmapEvent::MethodCall => "A JMAP method call has been made", + JmapEvent::InvalidArguments => "The JMAP arguments are invalid", + JmapEvent::RequestTooLarge => "The JMAP request is too large", + JmapEvent::StateMismatch => "The JMAP state is mismatched", + JmapEvent::AnchorNotFound => "The JMAP anchor was not found", + JmapEvent::UnsupportedFilter => "The JMAP filter is unsupported", + JmapEvent::UnsupportedSort => "The JMAP sort is unsupported", + JmapEvent::UnknownMethod => "The JMAP method is unknown", + JmapEvent::InvalidResultReference => "The JMAP result reference is invalid", + JmapEvent::Forbidden => "The JMAP operation is forbidden", + JmapEvent::AccountNotFound => "The JMAP account was not found", + JmapEvent::AccountNotSupportedByMethod => { + "The JMAP account is not supported by the method" + } + JmapEvent::AccountReadOnly => "The JMAP account is read-only", + JmapEvent::NotFound => "The JMAP resource was not found", + JmapEvent::CannotCalculateChanges => "Cannot calculate JMAP changes", + JmapEvent::UnknownDataType => "The JMAP data type is unknown", + JmapEvent::UnknownCapability => "The JMAP capability is unknown", + JmapEvent::NotJson => "The JMAP request is not JSON", + JmapEvent::NotRequest => "The JMAP input is not a request", + JmapEvent::WebsocketStart => "The JMAP WebSocket connection has started", + JmapEvent::WebsocketStop => "The JMAP WebSocket connection has stopped", + JmapEvent::WebsocketError => "An error occurred with the JMAP WebSocket connection", + } + } } impl LimitEvent { @@ -856,6 +1687,20 @@ impl LimitEvent { LimitEvent::TooManyRequests => "Too many requests", } } + + pub fn explain(&self) -> &'static str { + match self { + LimitEvent::SizeRequest => "The request size limit has been reached", + LimitEvent::SizeUpload => "The upload size limit has been reached", + LimitEvent::CallsIn => "The incoming calls limit has been reached", + LimitEvent::ConcurrentRequest => "The concurrent request limit has been reached", + LimitEvent::ConcurrentUpload => "The concurrent upload limit has been reached", + LimitEvent::ConcurrentConnection => "The concurrent connection limit has been reached", + LimitEvent::Quota => "The quota limit has been reached", + LimitEvent::BlobQuota => "The blob quota limit has been reached", + LimitEvent::TooManyRequests => "Too many requests have been made", + } + } } impl ManageEvent { @@ -869,6 +1714,17 @@ impl ManageEvent { ManageEvent::Error => "Management error", } } + + pub fn explain(&self) -> &'static str { + match self { + ManageEvent::MissingParameter => "A management parameter is missing", + ManageEvent::AlreadyExists => "The managed resource already exists", + ManageEvent::AssertFailed => "A management assertion has failed", + ManageEvent::NotFound => "The managed resource was not found", + ManageEvent::NotSupported => "The management operation is not supported", + ManageEvent::Error => "A management error occurred", + } + } } impl AuthEvent { @@ -882,6 +1738,19 @@ impl AuthEvent { AuthEvent::Error => "Authentication error", } } + + pub fn explain(&self) -> &'static str { + match self { + AuthEvent::Success => "Successful authentication", + AuthEvent::Failed => "Failed authentication", + AuthEvent::MissingTotp => "TOTP is missing for authentication", + AuthEvent::TooManyAttempts => "Too many authentication attempts have been made", + AuthEvent::Banned => { + "The IP address has been banned after multiple authentication failures" + } + AuthEvent::Error => "An error occurred with authentication", + } + } } impl ResourceEvent { @@ -894,4 +1763,14 @@ impl ResourceEvent { ResourceEvent::WebadminUnpacked => "Webadmin resource unpacked", } } + + pub fn explain(&self) -> &'static str { + match self { + ResourceEvent::NotFound => "The resource was not found", + ResourceEvent::BadParameters => "The resource parameters are bad", + ResourceEvent::Error => "An error occurred with the resource", + ResourceEvent::DownloadExternal => "The external resource is being downloaded", + ResourceEvent::WebadminUnpacked => "The webadmin resource has been unpacked", + } + } } diff --git a/crates/trc/src/event/level.rs b/crates/trc/src/event/level.rs index 42ee276e..4142f045 100644 --- a/crates/trc/src/event/level.rs +++ b/crates/trc/src/event/level.rs @@ -468,7 +468,7 @@ impl EventType { }, EventType::Queue(event) => match event { QueueEvent::QueueMessage - | QueueEvent::QueueMessageSubmission + | QueueEvent::QueueMessageAuthenticated | QueueEvent::QueueReport | QueueEvent::QueueDsn | QueueEvent::QueueAutogenerated diff --git a/crates/trc/src/event/mod.rs b/crates/trc/src/event/mod.rs index 4ff3cb1a..30e832cf 100644 --- a/crates/trc/src/event/mod.rs +++ b/crates/trc/src/event/mod.rs @@ -692,3 +692,9 @@ impl From for usize { value.id() } } + +impl AsRef> for Event { + fn as_ref(&self) -> &Event { + self + } +} diff --git a/crates/trc/src/ipc/metrics.rs b/crates/trc/src/ipc/metrics.rs index 558b30c9..b81c454f 100644 --- a/crates/trc/src/ipc/metrics.rs +++ b/crates/trc/src/ipc/metrics.rs @@ -232,7 +232,7 @@ impl Collector { EventType::Queue(QueueEvent::QueueMessage) => { MESSAGE_INCOMING_SIZE.observe(size); } - EventType::Queue(QueueEvent::QueueMessageSubmission) => { + EventType::Queue(QueueEvent::QueueMessageAuthenticated) => { MESSAGE_SUBMISSION_SIZE.observe(size); } EventType::Queue(QueueEvent::QueueReport) => { @@ -632,7 +632,7 @@ impl EventType { EventType::Delivery(_) => true, EventType::Queue( QueueEvent::QueueMessage - | QueueEvent::QueueMessageSubmission + | QueueEvent::QueueMessageAuthenticated | QueueEvent::QueueReport | QueueEvent::QueueDsn | QueueEvent::QueueAutogenerated diff --git a/crates/trc/src/lib.rs b/crates/trc/src/lib.rs index fa257a78..e6c877e8 100644 --- a/crates/trc/src/lib.rs +++ b/crates/trc/src/lib.rs @@ -466,7 +466,7 @@ pub enum DeliveryEvent { #[event_type] pub enum QueueEvent { QueueMessage, - QueueMessageSubmission, + QueueMessageAuthenticated, QueueReport, QueueDsn, QueueAutogenerated, diff --git a/crates/trc/src/serializers/binary.rs b/crates/trc/src/serializers/binary.rs index e00d81a6..0ba4f681 100644 --- a/crates/trc/src/serializers/binary.rs +++ b/crates/trc/src/serializers/binary.rs @@ -687,7 +687,7 @@ impl EventType { EventType::Queue(QueueEvent::QueueAutogenerated) => 378, EventType::Queue(QueueEvent::QueueDsn) => 379, EventType::Queue(QueueEvent::QueueMessage) => 380, - EventType::Queue(QueueEvent::QueueMessageSubmission) => 381, + EventType::Queue(QueueEvent::QueueMessageAuthenticated) => 381, EventType::Queue(QueueEvent::QueueReport) => 382, EventType::Queue(QueueEvent::QuotaExceeded) => 383, EventType::Queue(QueueEvent::RateLimitExceeded) => 384, @@ -1276,7 +1276,7 @@ impl EventType { 378 => Some(EventType::Queue(QueueEvent::QueueAutogenerated)), 379 => Some(EventType::Queue(QueueEvent::QueueDsn)), 380 => Some(EventType::Queue(QueueEvent::QueueMessage)), - 381 => Some(EventType::Queue(QueueEvent::QueueMessageSubmission)), + 381 => Some(EventType::Queue(QueueEvent::QueueMessageAuthenticated)), 382 => Some(EventType::Queue(QueueEvent::QueueReport)), 383 => Some(EventType::Queue(QueueEvent::QuotaExceeded)), 384 => Some(EventType::Queue(QueueEvent::RateLimitExceeded)), diff --git a/crates/trc/src/serializers/json.rs b/crates/trc/src/serializers/json.rs index fb7d0e81..cc36ed3f 100644 --- a/crates/trc/src/serializers/json.rs +++ b/crates/trc/src/serializers/json.rs @@ -4,104 +4,243 @@ * SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-SEL */ - use ahash::AHashSet; - use mail_parser::DateTime; - use serde::{ser::SerializeMap, Serialize, Serializer}; - - use crate::{Event, EventDetails, EventType, Key, Value}; - - struct Keys<'x> { - keys: &'x [(Key, Value)], - span_keys: &'x [(Key, Value)], - } - - impl Serialize for Event { - fn serialize(&self, serializer: S) -> Result - where - S: Serializer, - { - let mut map = serializer.serialize_map(Some(4))?; - map.serialize_entry( - "id", - &format!("{}{}", self.inner.timestamp, self.inner.typ.id()), - )?; - map.serialize_entry( - "createdAt", - &DateTime::from_timestamp(self.inner.timestamp as i64).to_rfc3339(), - )?; - map.serialize_entry("type", self.inner.typ.name())?; - map.serialize_entry( - "data", - &Keys { - keys: self.keys.as_slice(), - span_keys: self.inner.span.as_ref().map(|s| &s.keys[..]).unwrap_or(&[]), - }, - )?; - map.end() - } - } - - impl<'x> Serialize for Keys<'x> { - fn serialize(&self, serializer: S) -> Result - where - S: Serializer, - { - let keys_len = self.keys.len() + self.span_keys.len(); - let mut seen_keys = AHashSet::with_capacity(keys_len); - let mut keys = serializer.serialize_map(Some(keys_len))?; - for (key, value) in self.span_keys.iter().chain(self.keys.iter()) { - if !matches!(value, Value::None) - && !matches!(key, Key::SpanId) - && seen_keys.insert(*key) - { - keys.serialize_entry(key.name(), value)?; - } - } - keys.end() - } - } - - impl Serialize for Event { - fn serialize(&self, serializer: S) -> Result - where - S: Serializer, - { - let mut map = serializer.serialize_map(Some(4))?; - map.serialize_entry("type", self.inner.name())?; - map.serialize_entry( - "data", - &Keys { - keys: self.keys.as_slice(), - span_keys: &[], - }, - )?; - map.end() - } - } - - impl Serialize for Value { - fn serialize(&self, serializer: S) -> Result - where - S: Serializer, - { - match self { - Value::Static(value) => value.serialize(serializer), - Value::String(value) => value.serialize(serializer), - Value::UInt(value) => value.serialize(serializer), - Value::Int(value) => value.serialize(serializer), - Value::Float(value) => value.serialize(serializer), - Value::Timestamp(value) => DateTime::from_timestamp(*value as i64) - .to_rfc3339() - .serialize(serializer), - Value::Duration(value) => value.serialize(serializer), - Value::Bytes(value) => value.serialize(serializer), - Value::Bool(value) => value.serialize(serializer), - Value::Ipv4(value) => value.serialize(serializer), - Value::Ipv6(value) => value.serialize(serializer), - Value::Event(value) => value.serialize(serializer), - Value::Array(value) => value.serialize(serializer), - Value::None => unreachable!(), - } - } - } - \ No newline at end of file +use crate::{Event, EventDetails, EventType, Key, Value}; +use ahash::AHashSet; +use base64::{engine::general_purpose::STANDARD, Engine}; +use mail_parser::DateTime; +use serde::{ + ser::{SerializeMap, SerializeSeq}, + Serialize, Serializer, +}; + +struct Keys<'x> { + keys: &'x [(Key, Value)], + span_keys: &'x [(Key, Value)], +} + +pub struct JsonEventSerializer { + inner: T, + with_id: bool, + with_spans: bool, + with_description: bool, + with_explanation: bool, +} + +impl JsonEventSerializer { + pub fn new(inner: T) -> Self { + Self { + inner, + with_id: false, + with_spans: false, + with_description: false, + with_explanation: false, + } + } + + pub fn with_id(mut self) -> Self { + self.with_id = true; + self + } + + pub fn with_spans(mut self) -> Self { + self.with_spans = true; + self + } + + pub fn with_description(mut self) -> Self { + self.with_description = true; + self + } + + pub fn with_explanation(mut self) -> Self { + self.with_explanation = true; + self + } + + pub fn into_inner(self) -> T { + self.inner + } +} + +impl>> Serialize for JsonEventSerializer> { + fn serialize(&self, serializer: S) -> Result + where + S: Serializer, + { + let mut seq = serializer.serialize_seq(Some(self.inner.len()))?; + for event in &self.inner { + seq.serialize_element(&JsonEventSerializer { + inner: event, + with_id: self.with_id, + with_spans: self.with_spans, + with_description: self.with_description, + with_explanation: self.with_explanation, + })?; + } + seq.end() + } +} + +impl>> Serialize for JsonEventSerializer { + fn serialize(&self, serializer: S) -> Result + where + S: Serializer, + { + let event = self.inner.as_ref(); + let mut map = serializer.serialize_map(None)?; + if self.with_id { + map.serialize_entry( + "id", + &format!("{}{}", event.inner.timestamp, event.inner.typ.id()), + )?; + } + if self.with_description { + map.serialize_entry("text", event.inner.typ.description())?; + } + if self.with_explanation { + map.serialize_entry("details", event.inner.typ.explain())?; + } + map.serialize_entry( + "createdAt", + &DateTime::from_timestamp(event.inner.timestamp as i64).to_rfc3339(), + )?; + map.serialize_entry("type", event.inner.typ.name())?; + map.serialize_entry( + "data", + &JsonEventSerializer { + inner: Keys { + keys: event.keys.as_slice(), + span_keys: event + .inner + .span + .as_ref() + .map(|s| &s.keys[..]) + .unwrap_or(&[]), + }, + with_spans: self.with_spans, + with_description: self.with_description, + with_explanation: self.with_explanation, + with_id: self.with_id, + }, + )?; + map.end() + } +} + +impl<'x> Serialize for JsonEventSerializer> { + fn serialize(&self, serializer: S) -> Result + where + S: Serializer, + { + let keys_len = self.inner.keys.len() + self.inner.span_keys.len(); + let mut seen_keys = AHashSet::with_capacity(keys_len); + let mut keys = serializer.serialize_map(Some(keys_len))?; + for (key, value) in self.inner.span_keys.iter().chain(self.inner.keys.iter()) { + if !matches!(value, Value::None) + && (self.with_spans || !matches!(key, Key::SpanId)) + && seen_keys.insert(*key) + { + keys.serialize_entry( + key.name(), + &JsonEventSerializer { + inner: value, + with_spans: self.with_spans, + with_description: self.with_description, + with_explanation: self.with_explanation, + with_id: self.with_id, + }, + )?; + } + } + keys.end() + } +} + +impl Serialize for JsonEventSerializer<&Event> { + fn serialize(&self, serializer: S) -> Result + where + S: Serializer, + { + let mut map = serializer.serialize_map(None)?; + map.serialize_entry("type", self.inner.inner.name())?; + if self.with_description { + map.serialize_entry("text", self.inner.inner.description())?; + } + if self.with_explanation { + map.serialize_entry("details", self.inner.inner.explain())?; + } + map.serialize_entry( + "data", + &JsonEventSerializer { + inner: Keys { + keys: self.inner.keys.as_slice(), + span_keys: &[], + }, + with_spans: self.with_spans, + with_description: self.with_description, + with_explanation: self.with_explanation, + with_id: self.with_id, + }, + )?; + map.end() + } +} + +impl Serialize for JsonEventSerializer<&Value> { + fn serialize(&self, serializer: S) -> Result + where + S: Serializer, + { + match &self.inner { + Value::Static(value) => value.serialize(serializer), + Value::String(value) => value.serialize(serializer), + Value::UInt(value) => value.serialize(serializer), + Value::Int(value) => value.serialize(serializer), + Value::Float(value) => value.serialize(serializer), + Value::Timestamp(value) => DateTime::from_timestamp(*value as i64) + .to_rfc3339() + .serialize(serializer), + Value::Duration(value) => value.serialize(serializer), + Value::Bytes(value) => STANDARD.encode(value).serialize(serializer), + Value::Bool(value) => value.serialize(serializer), + Value::Ipv4(value) => value.serialize(serializer), + Value::Ipv6(value) => value.serialize(serializer), + Value::Event(value) => JsonEventSerializer { + inner: value, + with_spans: self.with_spans, + with_description: self.with_description, + with_explanation: self.with_explanation, + with_id: self.with_id, + } + .serialize(serializer), + Value::Array(value) => JsonEventSerializer { + inner: value, + with_spans: self.with_spans, + with_description: self.with_description, + with_explanation: self.with_explanation, + with_id: self.with_id, + } + .serialize(serializer), + Value::None => unreachable!(), + } + } +} + +impl Serialize for JsonEventSerializer<&Vec> { + fn serialize(&self, serializer: S) -> Result + where + S: Serializer, + { + let mut seq = serializer.serialize_seq(Some(self.inner.len()))?; + for value in self.inner { + seq.serialize_element(&JsonEventSerializer { + inner: value, + with_spans: self.with_spans, + with_description: self.with_description, + with_explanation: self.with_explanation, + with_id: self.with_id, + })?; + } + seq.end() + } +} diff --git a/crates/trc/src/serializers/text.rs b/crates/trc/src/serializers/text.rs index 0919e3d3..6e816e75 100644 --- a/crates/trc/src/serializers/text.rs +++ b/crates/trc/src/serializers/text.rs @@ -381,6 +381,7 @@ impl Display for Event { mod tests { use crate::{EventType, Level}; + #[allow(dead_code)] fn to_camel_case(name: &str) -> String { let mut out = String::with_capacity(name.len()); let mut upper = true; @@ -399,6 +400,7 @@ mod tests { out } + #[allow(dead_code)] fn event_to_class(name: &str) -> String { let (group, name) = name.split_once('.').unwrap(); let group = to_camel_case(group); @@ -410,6 +412,12 @@ mod tests { ) } + #[allow(dead_code)] + fn event_to_webadmin_class(name: &str) -> String { + let (group, name) = name.split_once('.').unwrap(); + format!("{}{}", to_camel_case(group), to_camel_case(name)) + } + #[test] fn print_all_events() { assert!(!Level::Disable.is_contained(Level::Warn)); @@ -435,8 +443,12 @@ mod tests { for (pos, (name, _, _)) in names.iter().enumerate() { //println!("{:?},", name); - //println!("{} => Some({}),", pos, event_to_class(name)); - println!("{} => {},", event_to_class(name), pos); + println!("{} => Some({}),", pos, event_to_class(name)); + //println!("{} => {},", event_to_class(name), pos); + /*println!( + "#[serde(rename = \"{name}\")]\n{},", + event_to_webadmin_class(name) + );*/ } } } diff --git a/crates/utils/Cargo.toml b/crates/utils/Cargo.toml index 72055013..f3941b97 100644 --- a/crates/utils/Cargo.toml +++ b/crates/utils/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "utils" -version = "0.9.1" +version = "0.9.2" edition = "2021" resolver = "2"