diff --git a/CHANGELOG.md b/CHANGELOG.md index 4eee501..a206bc3 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,15 @@ All notable changes to this project will be documented in this file. This project adheres to [Semantic Versioning](http://semver.org/). +## [1.0.5] - 2026-XX-XX + +### Added + +### Changed +- Replaced `base64` with `encodify`. + +### Fixed + ## [1.0.4] - 2026-09-10 ### Added diff --git a/Cargo.lock b/Cargo.lock index 0eea623..68a3095 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -86,7 +86,7 @@ dependencies = [ "proc-macro2", "quote", "syn 2.0.119", - "synstructure", + "synstructure 0.13.2", ] [[package]] @@ -167,7 +167,7 @@ checksum = "82f6aeea286b8eb4dd3431a1be1b59d290ace00f5bfd8e2a159bc2a05e2c1667" dependencies = [ "proc-macro2", "quote", - "syn 3.0.5", + "syn 3.0.6", ] [[package]] @@ -367,7 +367,7 @@ dependencies = [ "log", "num", "pin-project-lite", - "rand 0.10.2", + "rand 0.10.3", "rustls", "rustls-native-certs", "rustls-pki-types", @@ -453,9 +453,9 @@ checksum = "fc652a48c352aef3ea3aed32080501cf3ef6ed5da78602a020c991775b0aff04" [[package]] name = "cc" -version = "1.4.5" +version = "1.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "005ec2760ca554fae18df7a11195552ec576cd665632a881bc011d5bb2fd4d80" +checksum = "f360145194ee8e21db5ee7f3fcd4fe52210864c75c985dae33218202c8bbe040" dependencies = [ "find-msvc-tools", "jobserver", @@ -465,9 +465,9 @@ dependencies = [ [[package]] name = "cfg-if" -version = "1.0.4" +version = "1.0.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9330f8b2ff13f34540b44e946ef35111825727b38d33286ef986142615121801" +checksum = "4e7648175b45a9a48536d676f68d918270699102aa8dab5496df06904c914600" [[package]] name = "cfg_aliases" @@ -687,7 +687,7 @@ dependencies = [ "proc-macro2", "quote", "strsim", - "syn 3.0.5", + "syn 3.0.6", ] [[package]] @@ -698,7 +698,7 @@ checksum = "2ac7135c3ef02b2f7833bbeb1be5ba7f966dcde8a87c6b87f65a778d71a02785" dependencies = [ "darling_core", "quote", - "syn 3.0.5", + "syn 3.0.6", ] [[package]] @@ -799,7 +799,7 @@ checksum = "c6232dd377dcc64799954cbd3a9bb882e9cdc1308ccd87b1c098f1fb2eaf82a8" dependencies = [ "proc-macro2", "quote", - "syn 3.0.5", + "syn 3.0.6", ] [[package]] @@ -840,6 +840,16 @@ dependencies = [ "serde", ] +[[package]] +name = "encodify" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4fda1e5c788c2b59096e152ad1a76ce72a2ab80c75985eb94ca82de0b6555db7" +dependencies = [ + "memchr", + "simdutf8", +] + [[package]] name = "equivalent" version = "1.0.2" @@ -899,15 +909,15 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ee93edf3c501f0035bbeffeccfed0b79e14c311f12195ec0e661e114a0f60da4" dependencies = [ "portable-atomic", - "rand 0.10.2", + "rand 0.10.3", "web-time", ] [[package]] name = "find-msvc-tools" -version = "0.1.12" +version = "0.1.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3e0f1c7c3a72c66fd80abe965175f7523475c0489a87d3ff9d6e8c87d87a9d2d" +checksum = "aedcfb3409746eddb02b9e19ebda1c3394f759a152e48ee875a0844d1b955484" [[package]] name = "flume" @@ -1014,7 +1024,7 @@ checksum = "9fb9654ba8355388abeb8dcb4fc62f511300867002afc858860463bdd9fe0c44" dependencies = [ "proc-macro2", "quote", - "syn 3.0.5", + "syn 3.0.6", ] [[package]] @@ -1150,18 +1160,6 @@ version = "0.17.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ed5909b6e89a2db4456e54cd5f673791d7eca6732202bbf2a9cc504fe2f9b84a" -[[package]] -name = "hashify" -version = "0.2.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dd1246c0e5493286aeb2dde35b1f4eb9c4ce00e628641210a5e553fc001a1f26" -dependencies = [ - "indexmap 2.14.2", - "proc-macro2", - "quote", - "syn 2.0.119", -] - [[package]] name = "hashlink" version = "0.11.1" @@ -1318,9 +1316,9 @@ dependencies = [ [[package]] name = "hyper-rustls" -version = "0.27.9" +version = "0.27.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "33ca68d021ef39cf6463ab54c1d0f5daf03377b70561305bb89a8f83aab66e0f" +checksum = "dfa8e654703247911e29c23fbeaa261834bd9bb74efba2f9acddc37bfb127f53" dependencies = [ "http", "hyper", @@ -1346,16 +1344,17 @@ dependencies = [ [[package]] name = "hyper-util" -version = "0.1.20" +version = "0.1.21" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "96547c2556ec9d12fb1578c4eaf448b04993e7fb79cbaad930a656880a6bdfa0" +checksum = "ddc03d96684f9226b8a787cdb71488417b53ab5ea8fdb1dac946cb9431cc8bff" dependencies = [ - "base64 0.22.1", + "base64 0.23.1", "bytes", "futures-channel", "futures-util", "http", "http-body", + "httparse", "hyper", "ipnet", "libc", @@ -1562,9 +1561,9 @@ checksum = "8f42a60cbdf9a97f5d2305f08a87dc4e09308d1276d28c869c684d7777685682" [[package]] name = "jiff" -version = "0.2.35" +version = "0.2.37" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "668b7183bd07af9a4885f5c35b0cc5c83c4607a913c16b7e17291832910d2dcc" +checksum = "0ab1baf72f08796de0260609515130699b890ac25f30e610ad894bc5856cafdb" dependencies = [ "defmt", "jiff-core", @@ -1579,18 +1578,19 @@ dependencies = [ [[package]] name = "jiff-core" -version = "0.1.0" +version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7feca88439efe53da3754500c1851dedf3cb36c524dd5cf8225cc0794de95d09" +checksum = "5e52fe76043ccecc9005d2305ebaadf7d7fc0cc89ca6baa10a94d6bc68c7128c" dependencies = [ "defmt", + "log", ] [[package]] name = "jiff-static" -version = "0.2.35" +version = "0.2.37" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3a69dcb3a21cfb32ce1cd056169337ca284af0766dd766e7878819b251a49204" +checksum = "378268a1116ad67ae6228701118ac9f491d78fda38a40a1f1a9e1348de6f7212" dependencies = [ "jiff-core", "proc-macro2", @@ -1615,14 +1615,14 @@ dependencies = [ [[package]] name = "jmap-client" -version = "0.4.2" +version = "0.4.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4deab22e057d24e32122f0fc6e2d667a124fdd6a0d8ef3ed4f8a89923c11084f" +checksum = "f5b5bc66252cc8e779ef1238f40ab93971d54ad00b5d7afb5c1d447a9647ed62" dependencies = [ "ahash", "async-stream", - "base64 0.22.1", "chrono", + "encodify", "futures-util", "maybe-async", "parking_lot", @@ -1696,9 +1696,9 @@ dependencies = [ [[package]] name = "js-sys" -version = "0.3.105" +version = "0.3.106" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ce57d20d1ea864ce2ac172ab472d409214f4fd359f0b2a2775abdf522e2af99e" +checksum = "7883d941dae510fb2d978fc3fe018c71c9e2892fd38854de3e8b92c2e5ad9cc5" dependencies = [ "cfg-if", "futures-util", @@ -1757,18 +1757,9 @@ checksum = "f9f8bd3e56ce4dfc153cf470fffbfa98c7620958b312ca5c3a4b8d5181fd13c6" [[package]] name = "lru-slab" -version = "0.1.2" +version = "0.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "112b39cec0b298b6c1999fee3e31427f74f676e4cb9879ed1a121b43661a4154" - -[[package]] -name = "mail-parser" -version = "0.11.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4ec00bda90c6e645a54506c630c2820cd6b1890cfd2b0a169b50f74b2b8c7c86" -dependencies = [ - "hashify", -] +checksum = "4050469837a6ff301cd14c1f8f24f88549e6d548f24f64e2148eb0f72cebc51f" [[package]] name = "matchers" @@ -2219,16 +2210,15 @@ dependencies = [ [[package]] name = "proxy" -version = "1.0.4" +version = "1.0.5" dependencies = [ "ahash", "arc-swap", "aws-lc-rs", - "base64 0.23.1", + "encodify", "futures-util", "globset", "jmap-client", - "mail-parser", "mockito", "moka", "num_cpus", @@ -2260,9 +2250,9 @@ dependencies = [ [[package]] name = "proxy-header" -version = "0.1.2" +version = "0.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dc1493f63ddddfba840c3169e997c2905d09538ace72d64e84af6324c6e0e065" +checksum = "accf7ba1ae5e4f5b6acfb2d88d3cf68d03c333fca0260b06fb9aafcfcf22e2f5" dependencies = [ "pin-project-lite", "tokio", @@ -2285,9 +2275,9 @@ dependencies = [ [[package]] name = "quinn" -version = "0.11.11" +version = "0.11.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0c1a41e437b6bbd489372cd4971de128e85c855f56c57f283d20ff016cf7c0a8" +checksum = "4051e23e9185c255a7e33ef59cdbca87a22d359052eecd22fc6b901fb37d9d11" dependencies = [ "bytes", "cfg_aliases", @@ -2305,15 +2295,15 @@ dependencies = [ [[package]] name = "quinn-proto" -version = "0.11.17" +version = "0.11.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "04759210543be93709136e28212294a659ef5001836ff4eab4d663e4529bba83" +checksum = "a9746dbde176634f4f2f1faf2404e30a31b2bc1e9cafb5329c95d8177a18c9fc" dependencies = [ "aws-lc-rs", "bytes", "getrandom 0.4.3", "lru-slab", - "rand 0.10.2", + "rand 0.10.3", "rand_pcg", "ring", "rustc-hash", @@ -2384,9 +2374,9 @@ dependencies = [ [[package]] name = "rand" -version = "0.10.2" +version = "0.10.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c7f5fa3a058cd35567ef9bfa5e75732bee0f9e4c55fa90477bef2dfcdbc4be80" +checksum = "65c9fb96cbc91e3478eaae79a69fcd3f1ae4ad052e471fe6732fff548984b4af" dependencies = [ "chacha20", "getrandom 0.4.3", @@ -2462,9 +2452,9 @@ dependencies = [ [[package]] name = "redis" -version = "1.7.0" +version = "1.7.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2acbc41a996f7652b2ddd9dfd98cc4ff602cfd742ae35382f07f608405ab50ed" +checksum = "ed5b98ae99461e02895c3a96a25dbe4d852a3d5e5b815d31d5d9e4b5344d99e9" dependencies = [ "arc-swap", "arcstr", @@ -2514,7 +2504,7 @@ checksum = "92ecd8964f8453721699a1ed72037b0db49ce2f5a5138486ee89bed6f67cdf3a" dependencies = [ "proc-macro2", "quote", - "syn 3.0.5", + "syn 3.0.6", ] [[package]] @@ -2625,9 +2615,9 @@ dependencies = [ [[package]] name = "rustix" -version = "1.1.4" +version = "1.1.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b6fe4565b9518b83ef4f91bb47ce29620ca828bd32cb7e408f0062e9930ba190" +checksum = "891efababe418670775f199f0d233d84843c227a0949a883ce15b37c78d6629d" dependencies = [ "bitflags 2.13.2", "errno", @@ -2638,9 +2628,9 @@ dependencies = [ [[package]] name = "rustls" -version = "0.23.44" +version = "0.23.45" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6725596c3f2c3a0aef021139e145d4eafe314a6623e4680ca83852b2c67ab2ba" +checksum = "0d41d731c7d2f962d1ccc364cec258de3c0e93b38c2fb3ba97ac74513048d634" dependencies = [ "aws-lc-rs", "once_cell", @@ -2684,9 +2674,9 @@ dependencies = [ [[package]] name = "rustls-platform-verifier" -version = "0.7.0" +version = "0.7.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "26d1e2536ce4f35f4846aa13bff16bd0ff40157cdb14cc056c7b14ba41233ba0" +checksum = "1167586491e2b18b8bfbb293e8180ec17c201c4f076d7cb3070ca964e7598f98" dependencies = [ "core-foundation", "core-foundation-sys", @@ -2705,9 +2695,9 @@ dependencies = [ [[package]] name = "rustls-platform-verifier-android" -version = "0.1.1" +version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f87165f0995f63a9fbeea62b64d10b4d9d8e78ec6d7d51fb2125fda7bb36788f" +checksum = "eec689c0bc40ff2458a5977b6619cb718087084a18e02a131c599b62d05e1a5f" [[package]] name = "rustls-webpki" @@ -2837,7 +2827,7 @@ checksum = "e7a5d71263a5a7d47b41f6b3f06ba276f10cc18b0931f1799f710578e2309348" dependencies = [ "proc-macro2", "quote", - "syn 3.0.5", + "syn 3.0.6", ] [[package]] @@ -2861,7 +2851,7 @@ checksum = "8d3b1629de253c70a0508c3899572da79ca359fdab27c7920ff00406df418906" dependencies = [ "proc-macro2", "quote", - "syn 3.0.5", + "syn 3.0.6", ] [[package]] @@ -2887,9 +2877,9 @@ dependencies = [ [[package]] name = "serde_with" -version = "3.23.0" +version = "3.24.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "935177bb8c0cd8ca1a4e6d1a2ac8988bea69cab4f9d3a31311e012ad27868ea4" +checksum = "df9adc193c780ef8f159aee8b61e2d5801aaa555e6eb0947fe45530ec506296f" dependencies = [ "base64 0.23.1", "bs58", @@ -2908,14 +2898,14 @@ dependencies = [ [[package]] name = "serde_with_macros" -version = "3.23.0" +version = "3.24.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1d607aa01a3cb0ad757d6fd216136910db3c97b102fe686585689615a02dbcdc" +checksum = "3e17bbc68e28663bbbb90df47e058aa7eda4fb445b89fe70457bb94fbccf6e49" dependencies = [ "darling", "proc-macro2", "quote", - "syn 3.0.5", + "syn 3.0.6", ] [[package]] @@ -3049,9 +3039,9 @@ checksum = "0c790de23124f9ab44544d7ac05d60440adc586479ce501c1d6d7da3cd8c9cf5" [[package]] name = "smallvec" -version = "1.16.0" +version = "1.16.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b9be42f50aa861c555654aa3a37f52f4b1074bacf4e48fe0ef7fa584e80f1f0f" +checksum = "f9395f0f0eee849a9b707b2f06bb92a6a422090e2123bb2ef8e87a0e61892a8e" dependencies = [ "serde", ] @@ -3220,7 +3210,7 @@ dependencies = [ "log", "md-5 0.11.0", "memchr", - "rand 0.10.2", + "rand 0.10.3", "serde", "serde_json", "sha2 0.11.0", @@ -3321,9 +3311,9 @@ dependencies = [ [[package]] name = "syn" -version = "3.0.5" +version = "3.0.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "12df2e0110f65b775f769bb17ef989067a1d931b2eb822bd4346631eeada89f9" +checksum = "8593e8e72159ed2257d083c7a454a85cbf854f37a0966d8d483aff8c8a3ebcee" dependencies = [ "proc-macro2", "quote", @@ -3350,6 +3340,17 @@ dependencies = [ "syn 2.0.119", ] +[[package]] +name = "synstructure" +version = "0.14.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "901704edd0dfe137f1987838ee4f259e4e063c31371bdb423f7ae38ec6f77f02" +dependencies = [ + "proc-macro2", + "quote", + "syn 3.0.6", +] + [[package]] name = "tagptr" version = "0.2.0" @@ -3402,22 +3403,22 @@ dependencies = [ [[package]] name = "thiserror" -version = "2.0.20" +version = "2.0.21" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ec86235f5fcc2a73650310756d2ac5b138a5780bbbdfae3eeccec992c435ba4f" +checksum = "09e52cb86a36cede5cb101bf8908837b3e4c6e5e59fe7fd85c23fb56200d189e" dependencies = [ "thiserror-impl", ] [[package]] name = "thiserror-impl" -version = "2.0.20" +version = "2.0.21" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bc04cd3e1236dd4a98afca4569f2deb3f120e5422a4023be2cb683f8486292af" +checksum = "fe5197923287db20a58125f0bc85c062f7f2c892de97b18c356f9efb14b28524" dependencies = [ "proc-macro2", "quote", - "syn 3.0.5", + "syn 3.0.6", ] [[package]] @@ -3471,18 +3472,9 @@ dependencies = [ [[package]] name = "tinyvec" -version = "1.13.2" +version = "1.13.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4cf0ded5c4e56918d8f8a339e1bb67d038d3bc6d144ac407904015ba2e4cde9b" -dependencies = [ - "tinyvec_macros", -] - -[[package]] -name = "tinyvec_macros" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20" +checksum = "fd3ca314f692efd6c868f8408f53fe444634a845f96c028b97d35f6a1f79f0ee" [[package]] name = "tokio" @@ -3509,7 +3501,7 @@ checksum = "78773a2a397f451582ce068015985c33193cf6dea8b74d2a639fe457b2f07b0e" dependencies = [ "proc-macro2", "quote", - "syn 3.0.5", + "syn 3.0.6", ] [[package]] @@ -3565,9 +3557,9 @@ dependencies = [ [[package]] name = "toml" -version = "1.1.5+spec-1.1.0" +version = "1.1.6+spec-1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "12c0ba9680044b4ce98d391a62094047eada0d64860b80166c39f4a6b5640785" +checksum = "920602543f0911ab71da12c50d59701da54c196d1a2bf5cb4b75667f137a406a" dependencies = [ "indexmap 2.14.2", "serde_core", @@ -3791,9 +3783,9 @@ checksum = "5c1cb5db39152898a79168971543b1cb5020dff7fe43c8dc468b0885f5e29df5" [[package]] name = "unicode-ident" -version = "1.0.24" +version = "1.0.26" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e6e4313cd5fcd3dad5cafa179702e2b244f760991f45397d14d4ebf38247da75" +checksum = "d245f478577f809a851594d02313b640fb437e0bb33866753cff937863096954" [[package]] name = "unicode-normalization" @@ -3906,9 +3898,9 @@ dependencies = [ [[package]] name = "wasm-bindgen" -version = "0.2.128" +version = "0.2.129" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "aecb87a33d3b0c5e3b7aa46336eaf486cffafbd281b195e4c8b80d50df2351bf" +checksum = "9bb54f33acc68fd454578d9820b0bde1a1a3d17aa17bb7b6595806d02886d409" dependencies = [ "cfg-if", "once_cell", @@ -3919,19 +3911,20 @@ dependencies = [ [[package]] name = "wasm-bindgen-futures" -version = "0.4.78" +version = "0.4.79" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6ef4c5d3d2cdf5c54f4231181768f5510842e350db025faf1f7163b1030ed928" +checksum = "3cbab34de2d982e9b48e18d216d04c4a6f641066ff19ffb699980f591ee3610e" dependencies = [ "js-sys", + "tokio", "wasm-bindgen", ] [[package]] name = "wasm-bindgen-macro" -version = "0.2.128" +version = "0.2.129" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a690d511e3c1a8b3a55e33511e3c2c00c78415cd23650f32b808627f5696b9ed" +checksum = "2e29d0c35b16e224a7eeb5cd2d25e3e1968fbd65604117b44d3b789d00ee8535" dependencies = [ "quote", "wasm-bindgen-macro-support", @@ -3939,22 +3932,22 @@ dependencies = [ [[package]] name = "wasm-bindgen-macro-support" -version = "0.2.128" +version = "0.2.129" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "411e4887f0071ef2d2164a9d5fdf2d20efbef78fccd3a78b0c10a1dc5295e48a" +checksum = "6f501a8bc3719dba86ef8ae4728879c08001bea749eb1333ac5b91e040e2a6b7" dependencies = [ "bumpalo", "proc-macro2", "quote", - "syn 3.0.5", + "syn 3.0.6", "wasm-bindgen-shared", ] [[package]] name = "wasm-bindgen-shared" -version = "0.2.128" +version = "0.2.129" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "81941cd78d0c92026c33e5e01312845a4cb1e9af3407f9134b100dd03144103e" +checksum = "23f0c9c52aa7cd7d77769a4cfe2a9adb1b331f489a41d912ce14513d5ab995c6" dependencies = [ "unicode-ident", ] @@ -3974,9 +3967,9 @@ dependencies = [ [[package]] name = "web-sys" -version = "0.3.105" +version = "0.3.106" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9fbddc4a036f00ec4f18c83445bd3115cb306a91da554919a099d9222fe4a7f8" +checksum = "88261b9deccee56594c11a3460c462c41f58d148598fe70ad77070126a68aba4" dependencies = [ "js-sys", "wasm-bindgen", @@ -4263,30 +4256,30 @@ dependencies = [ [[package]] name = "yoke-derive" -version = "0.8.2" +version = "0.8.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "de844c262c8848816172cef550288e7dc6c7b7814b4ee56b3e1553f275f1858e" +checksum = "33811428bee40dbceb6d545e95754741d17a6aef9a4849f0fd62e2ba4f412a78" dependencies = [ "proc-macro2", "quote", - "syn 2.0.119", - "synstructure", + "syn 3.0.6", + "synstructure 0.14.0", ] [[package]] name = "zerocopy" -version = "0.8.57" +version = "0.8.59" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d35102a9f36d089ccae9e4c6802bc118be4487b80aaffc0ab4e0cf5ce92d2873" +checksum = "6df92bf3d9227be3d53173901ddbffac2babc27ae50f397776ffd6dc33f800cb" dependencies = [ "zerocopy-derive", ] [[package]] name = "zerocopy-derive" -version = "0.8.57" +version = "0.8.59" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "146c01f5ab44258da43cf276c74a2763db2ff3969c9c652c3f2de07041d0b2bc" +checksum = "ac4f328cf2f05d084e496c3e9c3f33ed0a183656a16e1fcec4d464d8373aec82" dependencies = [ "proc-macro2", "quote", @@ -4304,14 +4297,14 @@ dependencies = [ [[package]] name = "zerofrom-derive" -version = "0.1.7" +version = "0.1.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "11532158c46691caf0f2593ea8358fed6bbf68a0315e80aae9bd41fbade684a1" +checksum = "f75b4683f6c7f45248d4d64056a24298c6281e0993356d7d1b4a1a962ef10d4a" dependencies = [ "proc-macro2", "quote", - "syn 2.0.119", - "synstructure", + "syn 3.0.6", + "synstructure 0.14.0", ] [[package]] @@ -4350,7 +4343,7 @@ checksum = "34df6fc39dbd26ddc9c10e6a2984476e13acce22e64e4487636ef494369225da" dependencies = [ "proc-macro2", "quote", - "syn 3.0.5", + "syn 3.0.6", ] [[package]] diff --git a/Cargo.toml b/Cargo.toml index 63dffcc..42df42d 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "proxy" -version = "1.0.4" +version = "1.0.5" edition = "2024" license = "AGPL-3.0-only OR LicenseRef-SEL" description = "Multi-protocol e-mail proxy" @@ -22,10 +22,9 @@ tokio-rustls = { version = "0.26", default-features = false, features = ["aws_lc rustls-pemfile = "2.2" rustls-pki-types = "1.15" aws-lc-rs = "1.17" -proxy-header = { version = "0.1.2", features = ["tokio"] } +proxy-header = { version = "0.1", features = ["tokio"] } smtp-proto = "0.2.2" -mail-parser = "0.11" -base64 = "0.23" +encodify = "1.0" serde = { version = "1", features = ["derive"] } serde_json = "1" toml = "1" diff --git a/src/http/router.rs b/src/http/router.rs index 69d8e77..7c59f9a 100644 --- a/src/http/router.rs +++ b/src/http/router.rs @@ -6,7 +6,7 @@ use std::time::Duration; -use base64::{Engine, engine::general_purpose::STANDARD as BASE64}; +use encodify::base64::STANDARD as BASE64; use globset::{Glob, GlobSet, GlobSetBuilder}; use regex::Regex; @@ -144,8 +144,7 @@ impl HttpRouter { let value = head.header("authorization")?; let value = value.trim(); if let Some(rest) = strip_scheme(value, "basic") { - let decoded = BASE64.decode(rest.trim()).ok()?; - let text = String::from_utf8(decoded).ok()?; + let text = BASE64.decode_to_string(rest.trim()).ok()?; let login = match text.split_once(':') { Some((login, _)) => login, None => text.as_str(), @@ -359,7 +358,7 @@ extract = { from = "authorization" } #[test] fn bearer_uses_bearer_identifier() { - use base64::engine::general_purpose::URL_SAFE_NO_PAD; + use encodify::base64::URL_SAFE_NO_PAD; let router = router_from_toml( r#" [[route]] diff --git a/src/proto/common.rs b/src/proto/common.rs index a8f2337..21f503d 100644 --- a/src/proto/common.rs +++ b/src/proto/common.rs @@ -23,7 +23,7 @@ use crate::error::{ProxyError, Result}; use crate::imap_receiver::Mechanism; use crate::net::BoxedStream; use crate::net::tls; -use crate::sasl::Credential; +use crate::sasl::{Credential, SASL_RESPONSE}; pub struct Handoff { pub stream: BoxedStream, @@ -133,13 +133,15 @@ pub fn sasl_cancelled(response: &[u8]) -> bool { } pub fn login_challenge(username_b64: &[u8], password_b64: &[u8]) -> Option> { - let username = crate::sasl::decode_sasl_ir(username_b64)?; - let password = crate::sasl::decode_sasl_ir(password_b64)?; - let mut raw = Vec::with_capacity(username.len() + password.len() + 2); + let mut raw = Vec::with_capacity( + SASL_RESPONSE.decoded_len_estimate(username_b64.len()) + + SASL_RESPONSE.decoded_len_estimate(password_b64.len()) + + 2, + ); raw.push(0); - raw.extend_from_slice(&username); + SASL_RESPONSE.decode_append(username_b64, &mut raw).ok()?; raw.push(0); - raw.extend_from_slice(&password); + SASL_RESPONSE.decode_append(password_b64, &mut raw).ok()?; Some(raw) } @@ -1114,7 +1116,7 @@ mod tests { #[test] fn login_challenge_builds_a_plain_authentication_frame() { - use base64::{Engine, engine::general_purpose::STANDARD as BASE64}; + use encodify::base64::STANDARD as BASE64; let user = BASE64.encode("user@example.com"); let pass = BASE64.encode("secret"); assert_eq!( diff --git a/src/proto/imap.rs b/src/proto/imap.rs index 1a43de5..c3cadeb 100644 --- a/src/proto/imap.rs +++ b/src/proto/imap.rs @@ -6,7 +6,7 @@ use std::net::SocketAddr; -use base64::{Engine, engine::general_purpose::STANDARD as BASE64}; +use encodify::base64::STANDARD as BASE64; use tokio::io::AsyncReadExt; use tokio::time::timeout; @@ -19,7 +19,7 @@ use crate::proto::common::{ self, Ctx, assert_egress_encrypted, mechanism_name, plain_auth_allowed, read_crlf_line, write, }; use crate::proto::forward; -use crate::sasl::decode_sasl_ir; +use crate::sasl::SASL_RESPONSE; use crate::token::identifier_from_credential; const MAX_PREAUTH: usize = 64 * 1024; @@ -93,13 +93,15 @@ pub async fn handle( forward::ForwardInfo::new(peer, local, client_tls, None, ctx.config.server.proxy_ttl); let mech_name = mechanism_name(&mech); - let response = BASE64.encode(&raw_challenge); + let mut response = String::with_capacity(BASE64.encoded_len(raw_challenge.len()) + 2); + BASE64.encode_append(&raw_challenge, &mut response); + response.push_str("\r\n"); let auth = common::AuthFrame::inline( - format!("{tag} AUTHENTICATE {mech_name} {response}\r\n").into_bytes(), + format!("{tag} AUTHENTICATE {mech_name} {response}").into_bytes(), ) .with_deferred( format!("{tag} AUTHENTICATE {mech_name}\r\n").into_bytes(), - format!("{response}\r\n").into_bytes(), + response.into_bytes(), ); let unavailable = format!("{tag} NO [UNAVAILABLE] backend unavailable\r\n"); @@ -404,7 +406,7 @@ impl Auth<'_> { read_crlf_line(stream, seed, MAX_PREAUTH).await? }; - let Some(raw) = decode_sasl_ir(&raw_b64) else { + let Ok(raw) = SASL_RESPONSE.decode(&raw_b64) else { return self .reject( stream, diff --git a/src/proto/managesieve.rs b/src/proto/managesieve.rs index 5024f6f..7109a01 100644 --- a/src/proto/managesieve.rs +++ b/src/proto/managesieve.rs @@ -6,7 +6,7 @@ use std::net::SocketAddr; -use base64::{Engine, engine::general_purpose::STANDARD as BASE64}; +use encodify::base64::STANDARD as BASE64; use tokio::io::AsyncReadExt; use tokio::time::timeout; @@ -20,7 +20,7 @@ use crate::proto::common::{ plain_auth_allowed, write, }; use crate::proto::forward; -use crate::sasl::decode_sasl_ir; +use crate::sasl::SASL_RESPONSE; use crate::token::identifier_from_credential; const MAX_PREAUTH: usize = 64 * 1024; @@ -81,14 +81,10 @@ pub async fn handle( ctx.config.server.proxy_ttl, ); - let auth = common::AuthFrame::inline( - format!( - "AUTHENTICATE \"{}\" \"{}\"\r\n", - mechanism_name(&mech), - BASE64.encode(&raw_challenge) - ) - .into_bytes(), - ); + let mut frame = format!("AUTHENTICATE \"{}\" \"", mechanism_name(&mech)); + BASE64.encode_append(&raw_challenge, &mut frame); + frame.push_str("\"\r\n"); + let auth = common::AuthFrame::inline(frame.into_bytes()); let resume = common::bridge_authenticated( ctx, @@ -333,7 +329,7 @@ async fn handle_authenticate( read_sasl_response(stream, seed).await? }; - let Some(raw) = decode_sasl_ir(&raw_b64) else { + let Ok(raw) = SASL_RESPONSE.decode(&raw_b64) else { return fail( stream, "Invalid base64", diff --git a/src/proto/pop3.rs b/src/proto/pop3.rs index 5c925c3..a06c4fd 100644 --- a/src/proto/pop3.rs +++ b/src/proto/pop3.rs @@ -6,7 +6,7 @@ use std::net::SocketAddr; -use base64::{Engine, engine::general_purpose::STANDARD as BASE64}; +use encodify::base64::STANDARD as BASE64; use tokio::time::timeout; use crate::config::{Forwarding, ListenerConfig, Pop3Capabilities, Protocol, TlsMode}; @@ -19,7 +19,7 @@ use crate::proto::common::{ plain_auth_allowed, write, }; use crate::proto::forward; -use crate::sasl::decode_sasl_ir; +use crate::sasl::SASL_RESPONSE; use crate::token::identifier_from_credential; const MAX_PREAUTH: usize = 64 * 1024; @@ -71,14 +71,10 @@ pub async fn handle( ctx.config.server.proxy_ttl, ); - let auth = common::AuthFrame::inline( - format!( - "AUTH {} {}\r\n", - mechanism_name(&mech), - BASE64.encode(&raw_challenge) - ) - .into_bytes(), - ); + let mut frame = format!("AUTH {} ", mechanism_name(&mech)); + BASE64.encode_append(&raw_challenge, &mut frame); + frame.push_str("\r\n"); + let auth = common::AuthFrame::inline(frame.into_bytes()); let resume = common::bridge_authenticated( ctx, @@ -338,7 +334,7 @@ async fn preauth( line }; - let Some(raw) = decode_sasl_ir(&raw_b64) else { + let Ok(raw) = SASL_RESPONSE.decode(&raw_b64) else { write(&mut stream, b"-ERR invalid base64\r\n").await?; if bump(auth_failures, listener.max_auth_attempts) { return Ok(None); diff --git a/src/proto/smtp.rs b/src/proto/smtp.rs index f9a5299..fcadde7 100644 --- a/src/proto/smtp.rs +++ b/src/proto/smtp.rs @@ -6,7 +6,7 @@ use std::net::SocketAddr; -use base64::{Engine, engine::general_purpose::STANDARD as BASE64}; +use encodify::base64::STANDARD as BASE64; use smtp_proto::{ AUTH_LOGIN, AUTH_OAUTHBEARER, AUTH_PLAIN, AUTH_XOAUTH2, Error as SmtpError, Request, }; @@ -22,7 +22,7 @@ use crate::proto::common::{ plain_auth_allowed, write, }; use crate::proto::forward; -use crate::sasl::decode_sasl_ir; +use crate::sasl::SASL_RESPONSE; use crate::token::identifier_from_credential; const MAX_PREAUTH: usize = 64 * 1024; @@ -103,14 +103,10 @@ pub async fn handle_submission( ctx.config.server.proxy_ttl, ); - let auth = common::AuthFrame::inline( - format!( - "AUTH {} {}\r\n", - mechanism_name(&mech), - BASE64.encode(&raw_challenge) - ) - .into_bytes(), - ); + let mut frame = format!("AUTH {} ", mechanism_name(&mech)); + BASE64.encode_append(&raw_challenge, &mut frame); + frame.push_str("\r\n"); + let auth = common::AuthFrame::inline(frame.into_bytes()); let resume = common::bridge_authenticated( ctx, @@ -370,7 +366,7 @@ async fn handle_auth( line }; - let Some(raw) = decode_sasl_ir(&raw_b64) else { + let Ok(raw) = SASL_RESPONSE.decode(&raw_b64) else { return fail( stream, buf, diff --git a/src/sasl.rs b/src/sasl.rs index d157764..01583ce 100644 --- a/src/sasl.rs +++ b/src/sasl.rs @@ -4,6 +4,10 @@ * SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-SEL */ +use encodify::base64::{self, Base64}; + +pub const SASL_RESPONSE: Base64 = base64::LENIENT; + #[derive(Debug, Clone, PartialEq, Eq)] pub enum Credential { Plain { @@ -98,10 +102,6 @@ pub fn extract_oauth_bearer(bytes: &[u8]) -> Option<(String, Option)> { None } -pub fn decode_sasl_ir(b64: &[u8]) -> Option> { - mail_parser::decoders::base64::base64_decode(b64) -} - #[cfg(test)] mod tests { use super::*; @@ -154,7 +154,7 @@ mod tests { #[test] fn lenient_ir_decode() { - let out = decode_sasl_ir(b"dGVzdAB0ZXN0AHRlc3Q=").unwrap(); + let out = SASL_RESPONSE.decode(b"dGVzdAB0ZXN0AHRlc3Q=").unwrap(); assert_eq!(out, b"test\0test\0test"); } @@ -204,13 +204,13 @@ mod tests { #[test] fn lenient_ir_tolerates_whitespace() { - let with_ws = decode_sasl_ir(b"dGVz dAB0\nZXN0AHRlc3Q=").unwrap(); + let with_ws = SASL_RESPONSE.decode(b"dGVz dAB0\nZXN0AHRlc3Q=").unwrap(); assert_eq!(with_ws, b"test\0test\0test"); } #[test] fn lenient_ir_decodes_full_quads_without_padding() { - let out = decode_sasl_ir(b"AHRlc3QAc2VjcmV0").unwrap(); + let out = SASL_RESPONSE.decode(b"AHRlc3QAc2VjcmV0").unwrap(); assert_eq!(out, b"\0test\0secret"); } } diff --git a/src/tests/containers/clients.rs b/src/tests/containers/clients.rs index a5ea495..dc84c86 100644 --- a/src/tests/containers/clients.rs +++ b/src/tests/containers/clients.rs @@ -4,8 +4,7 @@ * SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-SEL */ -use base64::Engine; -use base64::engine::general_purpose::STANDARD as B64; +use encodify::base64::STANDARD as B64; use tokio::io::{AsyncRead, AsyncReadExt, AsyncWrite, AsyncWriteExt}; use tokio::net::TcpStream; diff --git a/src/tests/containers/http.rs b/src/tests/containers/http.rs index 9dcdfa8..b30f5ab 100644 --- a/src/tests/containers/http.rs +++ b/src/tests/containers/http.rs @@ -6,6 +6,7 @@ use std::sync::Arc; +use encodify::base64::STANDARD as B64; use tokio::io::{AsyncRead, AsyncReadExt, AsyncWrite, AsyncWriteExt}; use tokio::net::TcpStream; @@ -34,11 +35,9 @@ impl Response { } pub fn basic_auth(user: &str, pass: &str) -> String { - use base64::Engine; - format!( - "Basic {}", - base64::engine::general_purpose::STANDARD.encode(format!("{user}:{pass}")) - ) + let mut header = String::from("Basic "); + B64.encode_append(format!("{user}:{pass}"), &mut header); + header } pub async fn upgrade( diff --git a/src/tests/containers/master_user_and_load.rs b/src/tests/containers/master_user_and_load.rs index b0e628a..1e79341 100644 --- a/src/tests/containers/master_user_and_load.rs +++ b/src/tests/containers/master_user_and_load.rs @@ -4,8 +4,7 @@ * SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-SEL */ -use base64::Engine; -use base64::engine::general_purpose::STANDARD as B64; +use encodify::base64::STANDARD as B64; use tokio::io::{AsyncReadExt, AsyncWriteExt}; use tokio::net::TcpStream; diff --git a/src/tests/containers/oauth.rs b/src/tests/containers/oauth.rs index f90bd86..47ac9e8 100644 --- a/src/tests/containers/oauth.rs +++ b/src/tests/containers/oauth.rs @@ -4,8 +4,7 @@ * SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-SEL */ -use base64::Engine; -use base64::engine::general_purpose::STANDARD as B64; +use encodify::base64::STANDARD as B64; use tokio::io::{AsyncRead, AsyncReadExt, AsyncWriteExt}; use tokio::net::TcpStream; diff --git a/src/tests/containers/stalwart.rs b/src/tests/containers/stalwart.rs index c3acb01..8889b44 100644 --- a/src/tests/containers/stalwart.rs +++ b/src/tests/containers/stalwart.rs @@ -830,7 +830,7 @@ fn path_of(url: &str) -> String { #[cfg(test)] mod tests { use super::*; - use base64::Engine; + use encodify::base64::STANDARD as B64; use tokio::io::{AsyncReadExt, AsyncWriteExt}; async fn imaps_auth(host: &str, port: u16, login: &str, password: &str) -> String { @@ -838,7 +838,7 @@ mod tests { let mut stream = crate::tests::harness::tls_connect_client(tcp).await; let mut buf = [0u8; 512]; let _ = stream.read(&mut buf).await.unwrap(); - let ir = base64::engine::general_purpose::STANDARD.encode(format!("\0{login}\0{password}")); + let ir = B64.encode(format!("\0{login}\0{password}")); stream .write_all(format!("a1 AUTHENTICATE PLAIN {ir}\r\n").as_bytes()) .await diff --git a/src/tests/forwarding.rs b/src/tests/forwarding.rs index ffc928f..c9877a7 100644 --- a/src/tests/forwarding.rs +++ b/src/tests/forwarding.rs @@ -4,7 +4,7 @@ * SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-SEL */ -use base64::{Engine, engine::general_purpose::STANDARD as BASE64}; +use encodify::base64::STANDARD as BASE64; use tokio::io::{AsyncReadExt, AsyncWriteExt}; use tokio::net::TcpStream; diff --git a/src/tests/http_proxy.rs b/src/tests/http_proxy.rs index 73999c9..db192c6 100644 --- a/src/tests/http_proxy.rs +++ b/src/tests/http_proxy.rs @@ -4,7 +4,7 @@ * SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-SEL */ -use base64::{Engine, engine::general_purpose::STANDARD as BASE64}; +use encodify::base64::STANDARD as BASE64; use super::harness::*; diff --git a/src/tests/imap.rs b/src/tests/imap.rs index d2cf8db..9fdbab3 100644 --- a/src/tests/imap.rs +++ b/src/tests/imap.rs @@ -4,7 +4,7 @@ * SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-SEL */ -use base64::{Engine, engine::general_purpose::STANDARD as BASE64}; +use encodify::base64::STANDARD as BASE64; use tokio::io::{AsyncReadExt, AsyncWriteExt}; use super::harness::*; diff --git a/src/tests/managesieve.rs b/src/tests/managesieve.rs index 1763bab..b37ba36 100644 --- a/src/tests/managesieve.rs +++ b/src/tests/managesieve.rs @@ -4,7 +4,7 @@ * SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-SEL */ -use base64::{Engine, engine::general_purpose::STANDARD as BASE64}; +use encodify::base64::STANDARD as BASE64; use tokio::io::{AsyncReadExt, AsyncWriteExt}; use super::harness::*; diff --git a/src/tests/pop3.rs b/src/tests/pop3.rs index d2fe17e..d5a9006 100644 --- a/src/tests/pop3.rs +++ b/src/tests/pop3.rs @@ -4,7 +4,7 @@ * SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-SEL */ -use base64::{Engine, engine::general_purpose::STANDARD as BASE64}; +use encodify::base64::STANDARD as BASE64; use tokio::io::{AsyncReadExt, AsyncWriteExt}; use super::harness::*; diff --git a/src/tests/smtp.rs b/src/tests/smtp.rs index 01444cd..10e6a22 100644 --- a/src/tests/smtp.rs +++ b/src/tests/smtp.rs @@ -4,7 +4,7 @@ * SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-SEL */ -use base64::{Engine, engine::general_purpose::STANDARD as BASE64}; +use encodify::base64::STANDARD as BASE64; use tokio::io::{AsyncReadExt, AsyncWriteExt}; use super::harness::*; diff --git a/src/token.rs b/src/token.rs index 6c9117e..3955b07 100644 --- a/src/token.rs +++ b/src/token.rs @@ -4,17 +4,14 @@ * SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-SEL */ -use base64::{Engine, engine::general_purpose::URL_SAFE_NO_PAD}; +use encodify::base64::URL_SAFE_NO_PAD; use crate::sasl::Credential; pub fn bearer_identifier(token: &str, jwt_username_claim: &str) -> Option { if let Some(rest) = token.strip_prefix("sw1.") { return match rest.split_once('.') { - Some((_body, footer)) => { - let decoded = URL_SAFE_NO_PAD.decode(footer).ok()?; - String::from_utf8(decoded).ok() - } + Some((_body, footer)) => URL_SAFE_NO_PAD.decode_to_string(footer).ok(), None => None, }; } @@ -61,7 +58,6 @@ pub fn identifier_from_credential(cred: &Credential, jwt_username_claim: &str) - #[cfg(test)] mod tests { use super::*; - use base64::{Engine, engine::general_purpose::URL_SAFE_NO_PAD}; #[test] fn sw1_footer() {