mirror of
https://github.com/stalwartlabs/mail-server.git
synced 2024-09-20 07:16:18 +08:00
Branding + fixes
This commit is contained in:
parent
e9d12aea44
commit
a67f308645
322
Cargo.lock
generated
322
Cargo.lock
generated
|
@ -4,19 +4,13 @@ version = 3
|
|||
|
||||
[[package]]
|
||||
name = "addr2line"
|
||||
version = "0.22.0"
|
||||
version = "0.24.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6e4503c46a5c0c7844e948c9a4d6acd9f50cccb4de1c48eb9e291ea17470c678"
|
||||
checksum = "f5fb1d8e4442bd405fdfd1dacb42792696b0cf9cb15882e5d097b742a676d375"
|
||||
dependencies = [
|
||||
"gimli",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "adler"
|
||||
version = "1.0.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe"
|
||||
|
||||
[[package]]
|
||||
name = "adler2"
|
||||
version = "2.0.0"
|
||||
|
@ -180,9 +174,9 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "anyhow"
|
||||
version = "1.0.86"
|
||||
version = "1.0.89"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b3d1d046238990b9cf5bcde22a3fb3584ee5cf65fb2765f454ed428c7a0063da"
|
||||
checksum = "86fdf8605db99b54d3cd748a44c6d04df638eb5dafb219b135d0149bd0db01f6"
|
||||
|
||||
[[package]]
|
||||
name = "arbitrary"
|
||||
|
@ -213,9 +207,9 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "arrayref"
|
||||
version = "0.3.8"
|
||||
version = "0.3.9"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9d151e35f61089500b617991b791fc8bfd237ae50cd5950803758a179b41e67a"
|
||||
checksum = "76a2e8124351fda1ef8aaaa3bbd7ebbcb486bbcd4225aca0aa0d84bb2db8fecb"
|
||||
|
||||
[[package]]
|
||||
name = "arrayvec"
|
||||
|
@ -385,7 +379,7 @@ dependencies = [
|
|||
"serde",
|
||||
"serde_json",
|
||||
"url",
|
||||
"webpki-roots 0.26.5",
|
||||
"webpki-roots 0.26.6",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
@ -469,17 +463,17 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "backtrace"
|
||||
version = "0.3.73"
|
||||
version = "0.3.74"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5cc23269a4f8976d0a4d2e7109211a419fe30e8d88d677cd60b6bc79c5732e0a"
|
||||
checksum = "8d82cb332cdfaed17ae235a638438ac4d4839913cc2af585c3c6746e8f8bee1a"
|
||||
dependencies = [
|
||||
"addr2line",
|
||||
"cc",
|
||||
"cfg-if",
|
||||
"libc",
|
||||
"miniz_oxide 0.7.4",
|
||||
"miniz_oxide",
|
||||
"object",
|
||||
"rustc-demangle",
|
||||
"windows-targets 0.52.6",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
@ -788,9 +782,9 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "bytemuck"
|
||||
version = "1.17.1"
|
||||
version = "1.18.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "773d90827bc3feecfb67fab12e24de0749aad83c74b9504ecde46237b5cd24e2"
|
||||
checksum = "94bbb0ad554ad961ddc5da507a12a29b14e4ae5bda06b19f575a3e6079d2e2ae"
|
||||
|
||||
[[package]]
|
||||
name = "byteorder"
|
||||
|
@ -800,9 +794,9 @@ checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b"
|
|||
|
||||
[[package]]
|
||||
name = "bytes"
|
||||
version = "1.7.1"
|
||||
version = "1.7.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8318a53db07bb3f8dca91a600466bdb3f2eaadeedfdbcf02e1accbad9271ba50"
|
||||
checksum = "428d9aa8fbc0670b7b8d6030a7fadd0f86151cae55e4dbbece15f3780a3dfaf3"
|
||||
|
||||
[[package]]
|
||||
name = "bzip2"
|
||||
|
@ -855,9 +849,9 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "cc"
|
||||
version = "1.1.15"
|
||||
version = "1.1.21"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "57b6a275aa2903740dc87da01c62040406b8812552e97129a63ea8850a17c6e6"
|
||||
checksum = "07b1695e2c7e8fc85310cde85aeaab7e3097f593c91d209d3f9df76c928100f0"
|
||||
dependencies = [
|
||||
"jobserver",
|
||||
"libc",
|
||||
|
@ -962,9 +956,9 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "clap"
|
||||
version = "4.5.16"
|
||||
version = "4.5.17"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ed6719fffa43d0d87e5fd8caeab59be1554fb028cd30edc88fc4369b17971019"
|
||||
checksum = "3e5a21b8495e732f1b3c364c9949b201ca7bae518c502c80256c96ad79eaf6ac"
|
||||
dependencies = [
|
||||
"clap_builder",
|
||||
"clap_derive",
|
||||
|
@ -972,9 +966,9 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "clap_builder"
|
||||
version = "4.5.15"
|
||||
version = "4.5.17"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "216aec2b177652e3846684cbfe25c9964d18ec45234f0f5da5157b207ed1aab6"
|
||||
checksum = "8cf2dd12af7a047ad9d6da2b6b249759a22a7abc0f474c1dae1777afa4b21a73"
|
||||
dependencies = [
|
||||
"anstream",
|
||||
"anstyle",
|
||||
|
@ -1042,7 +1036,7 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "common"
|
||||
version = "0.9.4"
|
||||
version = "0.10.0"
|
||||
dependencies = [
|
||||
"ahash 0.8.11",
|
||||
"arc-swap",
|
||||
|
@ -1075,12 +1069,13 @@ dependencies = [
|
|||
"privdrop",
|
||||
"prometheus",
|
||||
"proxy-header",
|
||||
"psl",
|
||||
"pwhash",
|
||||
"rcgen 0.12.1",
|
||||
"regex",
|
||||
"reqwest 0.12.7",
|
||||
"ring 0.17.8",
|
||||
"rustls 0.23.12",
|
||||
"rustls 0.23.13",
|
||||
"rustls-pemfile 2.1.3",
|
||||
"rustls-pki-types",
|
||||
"serde",
|
||||
|
@ -1176,9 +1171,9 @@ checksum = "773648b94d0e5d620f64f280777445740e61fe701025087ec8b57f45c791888b"
|
|||
|
||||
[[package]]
|
||||
name = "cpufeatures"
|
||||
version = "0.2.13"
|
||||
version = "0.2.14"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "51e852e6dc9a5bed1fae92dd2375037bf2b768725bf3be87811edee3249d09ad"
|
||||
checksum = "608697df725056feaccfa42cffdaeeec3fccc4ffc38358ecd19b243e716a78e0"
|
||||
dependencies = [
|
||||
"libc",
|
||||
]
|
||||
|
@ -1437,9 +1432,9 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "dashmap"
|
||||
version = "6.0.1"
|
||||
version = "6.1.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "804c8821570c3f8b70230c2ba75ffa5c0f9a4189b9a432b6656c536712acae28"
|
||||
checksum = "5041cc499144891f3790297212f32a74fb938e5136a14943f338ef9e0ae276cf"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"crossbeam-utils",
|
||||
|
@ -1650,7 +1645,7 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "directory"
|
||||
version = "0.9.4"
|
||||
version = "0.10.0"
|
||||
dependencies = [
|
||||
"ahash 0.8.11",
|
||||
"argon2",
|
||||
|
@ -1670,7 +1665,7 @@ dependencies = [
|
|||
"proc_macros",
|
||||
"pwhash",
|
||||
"regex",
|
||||
"rustls 0.23.12",
|
||||
"rustls 0.23.13",
|
||||
"rustls-pki-types",
|
||||
"scrypt",
|
||||
"serde",
|
||||
|
@ -1932,11 +1927,11 @@ checksum = "c34f04666d835ff5d62e058c3995147c06f42fe86ff053337632bca83e42702d"
|
|||
|
||||
[[package]]
|
||||
name = "enum-as-inner"
|
||||
version = "0.6.0"
|
||||
version = "0.6.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5ffccbb6966c05b32ef8fbac435df276c4ae4d3dc55a8cd0eb9745e6c12f546a"
|
||||
checksum = "a1e6a265c649f3f5979b601d26f1d05ada116434c87741c9493cb56218f76cbc"
|
||||
dependencies = [
|
||||
"heck 0.4.1",
|
||||
"heck 0.5.0",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.77",
|
||||
|
@ -2038,7 +2033,7 @@ checksum = "324a1be68054ef05ad64b861cc9eaf1d623d2d8cb25b4bf2cb9cdd902b4bf253"
|
|||
dependencies = [
|
||||
"crc32fast",
|
||||
"libz-sys",
|
||||
"miniz_oxide 0.8.0",
|
||||
"miniz_oxide",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
@ -2354,9 +2349,9 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "gimli"
|
||||
version = "0.29.0"
|
||||
version = "0.31.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "40ecd4077b5ae9fd2e9e169b102c6c330d0605168eb0e8bf79952b256dbefffd"
|
||||
checksum = "32085ea23f3234fc7846555e85283ba4de91e21016dc0455a16286d87a292d64"
|
||||
|
||||
[[package]]
|
||||
name = "glob"
|
||||
|
@ -2739,20 +2734,20 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "hyper-rustls"
|
||||
version = "0.27.2"
|
||||
version = "0.27.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5ee4be2c948921a1a5320b629c4193916ed787a7f7f293fd3f7f5a6c9de74155"
|
||||
checksum = "08afdbb5c31130e3034af566421053ab03787c640246a446327f550d11bcb333"
|
||||
dependencies = [
|
||||
"futures-util",
|
||||
"http 1.1.0",
|
||||
"hyper 1.4.1",
|
||||
"hyper-util",
|
||||
"rustls 0.23.12",
|
||||
"rustls 0.23.13",
|
||||
"rustls-pki-types",
|
||||
"tokio",
|
||||
"tokio-rustls 0.26.0",
|
||||
"tower-service",
|
||||
"webpki-roots 0.26.5",
|
||||
"webpki-roots 0.26.6",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
@ -2770,9 +2765,9 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "hyper-util"
|
||||
version = "0.1.7"
|
||||
version = "0.1.8"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "cde7055719c54e36e95e8719f95883f22072a48ede39db7fc17a4e1d5281e9b9"
|
||||
checksum = "da62f120a8a37763efb0cf8fdf264b884c7b8b9ac8660b900c8661030c00e6ba"
|
||||
dependencies = [
|
||||
"bytes",
|
||||
"futures-channel",
|
||||
|
@ -2790,9 +2785,9 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "iana-time-zone"
|
||||
version = "0.1.60"
|
||||
version = "0.1.61"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e7ffbb5a1b541ea2561f8c41c087286cc091e21e556a4f09a8f6cbf17b69b141"
|
||||
checksum = "235e081f3925a06703c2d0117ea8b91f042756fd6e7a6e5d901e8ca1a996b220"
|
||||
dependencies = [
|
||||
"android_system_properties",
|
||||
"core-foundation-sys",
|
||||
|
@ -2984,7 +2979,7 @@ checksum = "edcd27d72f2f071c64249075f42e205ff93c9a4c5f6c6da53e79ed9f9832c285"
|
|||
|
||||
[[package]]
|
||||
name = "imap"
|
||||
version = "0.9.4"
|
||||
version = "0.10.0"
|
||||
dependencies = [
|
||||
"ahash 0.8.11",
|
||||
"common",
|
||||
|
@ -2999,7 +2994,7 @@ dependencies = [
|
|||
"nlp",
|
||||
"parking_lot",
|
||||
"rand",
|
||||
"rustls 0.23.12",
|
||||
"rustls 0.23.13",
|
||||
"rustls-pemfile 2.1.3",
|
||||
"store",
|
||||
"tokio",
|
||||
|
@ -3096,9 +3091,9 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "ipnet"
|
||||
version = "2.9.0"
|
||||
version = "2.10.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8f518f335dce6725a761382244631d86cf0ccb2863413590b31338feb467f9c3"
|
||||
checksum = "187674a687eed5fe42285b40c6291f9a01517d415fad1c3cbc6a9f778af7fcd4"
|
||||
|
||||
[[package]]
|
||||
name = "is-terminal"
|
||||
|
@ -3196,7 +3191,7 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "jmap"
|
||||
version = "0.9.4"
|
||||
version = "0.10.0"
|
||||
dependencies = [
|
||||
"aes",
|
||||
"aes-gcm",
|
||||
|
@ -3549,9 +3544,9 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "lz4-sys"
|
||||
version = "1.10.0"
|
||||
version = "1.11.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "109de74d5d2353660401699a4174a4ff23fcc649caf553df71933c7fb45ad868"
|
||||
checksum = "fcb44a01837a858d47e5a630d2ccf304c8efcc4b83b8f9f75b7a9ee4fcc6e57d"
|
||||
dependencies = [
|
||||
"cc",
|
||||
"libc",
|
||||
|
@ -3575,9 +3570,9 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "mail-auth"
|
||||
version = "0.4.3"
|
||||
version = "0.5.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9bd9d657de66a3d5ac360c3eab8c9f5cac2565f2b97cc032d5de4c900ef470de"
|
||||
checksum = "aaee4c38f4df428c6732f3d5472a013fa248d2772f48c8932295b32c683a23c4"
|
||||
dependencies = [
|
||||
"ahash 0.8.11",
|
||||
"flate2",
|
||||
|
@ -3586,7 +3581,7 @@ dependencies = [
|
|||
"mail-builder",
|
||||
"mail-parser",
|
||||
"parking_lot",
|
||||
"quick-xml 0.32.0",
|
||||
"quick-xml 0.36.1",
|
||||
"rand",
|
||||
"ring 0.17.8",
|
||||
"rsa",
|
||||
|
@ -3607,9 +3602,9 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "mail-parser"
|
||||
version = "0.9.3"
|
||||
version = "0.9.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ed5a1335c3a964788c90cb42ae04a34b5f2628e89566949ce3bd4ada695c0bcd"
|
||||
checksum = "93c3b9e5d8b17faf573330bbc43b37d6e918c0a3bf8a88e7d0a220ebc84af9fc"
|
||||
dependencies = [
|
||||
"encoding_rs",
|
||||
"serde",
|
||||
|
@ -3624,17 +3619,17 @@ dependencies = [
|
|||
"base64 0.22.1",
|
||||
"gethostname",
|
||||
"md5",
|
||||
"rustls 0.23.12",
|
||||
"rustls 0.23.13",
|
||||
"rustls-pki-types",
|
||||
"smtp-proto",
|
||||
"tokio",
|
||||
"tokio-rustls 0.26.0",
|
||||
"webpki-roots 0.26.5",
|
||||
"webpki-roots 0.26.6",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "mail-server"
|
||||
version = "0.9.4"
|
||||
version = "0.10.0"
|
||||
dependencies = [
|
||||
"common",
|
||||
"directory",
|
||||
|
@ -3653,7 +3648,7 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "managesieve"
|
||||
version = "0.9.4"
|
||||
version = "0.10.0"
|
||||
dependencies = [
|
||||
"ahash 0.8.11",
|
||||
"bincode",
|
||||
|
@ -3667,7 +3662,7 @@ dependencies = [
|
|||
"mail-send",
|
||||
"md5",
|
||||
"parking_lot",
|
||||
"rustls 0.23.12",
|
||||
"rustls 0.23.13",
|
||||
"rustls-pemfile 2.1.3",
|
||||
"sieve-rs",
|
||||
"store",
|
||||
|
@ -3786,15 +3781,6 @@ version = "0.2.1"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "68354c5c6bd36d73ff3feceb05efa59b6acb7626617f4962be322a825e61f79a"
|
||||
|
||||
[[package]]
|
||||
name = "miniz_oxide"
|
||||
version = "0.7.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b8a240ddb74feaf34a79a7add65a741f3167852fba007066dcac1ca548d89c08"
|
||||
dependencies = [
|
||||
"adler",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "miniz_oxide"
|
||||
version = "0.8.0"
|
||||
|
@ -3880,7 +3866,7 @@ dependencies = [
|
|||
"twox-hash",
|
||||
"url",
|
||||
"webpki",
|
||||
"webpki-roots 0.26.5",
|
||||
"webpki-roots 0.26.6",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
@ -3952,7 +3938,7 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "nlp"
|
||||
version = "0.9.4"
|
||||
version = "0.10.0"
|
||||
dependencies = [
|
||||
"ahash 0.8.11",
|
||||
"bincode",
|
||||
|
@ -3962,12 +3948,12 @@ dependencies = [
|
|||
"nohash",
|
||||
"parking_lot",
|
||||
"phf",
|
||||
"psl",
|
||||
"rust-stemmers",
|
||||
"serde",
|
||||
"siphasher 1.0.1",
|
||||
"tinysegmenter",
|
||||
"tokio",
|
||||
"utils",
|
||||
"whatlang",
|
||||
"xxhash-rust",
|
||||
]
|
||||
|
@ -4300,7 +4286,7 @@ checksum = "1e401f977ab385c9e4e3ab30627d6f26d00e2c73eef317493c4ec6d468726cf8"
|
|||
dependencies = [
|
||||
"cfg-if",
|
||||
"libc",
|
||||
"redox_syscall 0.5.3",
|
||||
"redox_syscall",
|
||||
"smallvec",
|
||||
"windows-targets 0.52.6",
|
||||
]
|
||||
|
@ -4503,7 +4489,7 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "pop3"
|
||||
version = "0.9.4"
|
||||
version = "0.10.0"
|
||||
dependencies = [
|
||||
"common",
|
||||
"directory",
|
||||
|
@ -4512,7 +4498,7 @@ dependencies = [
|
|||
"jmap_proto",
|
||||
"mail-parser",
|
||||
"mail-send",
|
||||
"rustls 0.23.12",
|
||||
"rustls 0.23.13",
|
||||
"store",
|
||||
"tokio",
|
||||
"tokio-rustls 0.26.0",
|
||||
|
@ -4546,9 +4532,9 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "postgres-types"
|
||||
version = "0.2.7"
|
||||
version = "0.2.8"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "02048d9e032fb3cc3413bbf7b83a15d84a5d419778e2628751896d856498eee9"
|
||||
checksum = "f66ea23a2d0e5734297357705193335e0a957696f34bed2f2faefacb2fec336f"
|
||||
dependencies = [
|
||||
"bytes",
|
||||
"fallible-iterator 0.2.0",
|
||||
|
@ -4717,6 +4703,21 @@ dependencies = [
|
|||
"tokio",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "psl"
|
||||
version = "2.1.55"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ce9398ad066421139b2e3afe16ea46772ffda30bd9ba57554dc035df5e26edc8"
|
||||
dependencies = [
|
||||
"psl-types",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "psl-types"
|
||||
version = "2.0.11"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "33cb294fe86a74cbcf50d4445b37da762029549ebeea341421c7c70370f86cac"
|
||||
|
||||
[[package]]
|
||||
name = "ptr_meta"
|
||||
version = "0.1.4"
|
||||
|
@ -4779,16 +4780,16 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "quinn"
|
||||
version = "0.11.3"
|
||||
version = "0.11.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b22d8e7369034b9a7132bc2008cac12f2013c8132b45e0554e6e20e2617f2156"
|
||||
checksum = "8c7c5fdde3cdae7203427dc4f0a68fe0ed09833edc525a03456b153b79828684"
|
||||
dependencies = [
|
||||
"bytes",
|
||||
"pin-project-lite",
|
||||
"quinn-proto",
|
||||
"quinn-udp",
|
||||
"rustc-hash 2.0.0",
|
||||
"rustls 0.23.12",
|
||||
"rustls 0.23.13",
|
||||
"socket2",
|
||||
"thiserror",
|
||||
"tokio",
|
||||
|
@ -4797,15 +4798,15 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "quinn-proto"
|
||||
version = "0.11.6"
|
||||
version = "0.11.8"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ba92fb39ec7ad06ca2582c0ca834dfeadcaf06ddfc8e635c80aa7e1c05315fdd"
|
||||
checksum = "fadfaed2cd7f389d0161bb73eeb07b7b78f8691047a6f3e73caaeae55310a4a6"
|
||||
dependencies = [
|
||||
"bytes",
|
||||
"rand",
|
||||
"ring 0.17.8",
|
||||
"rustc-hash 2.0.0",
|
||||
"rustls 0.23.12",
|
||||
"rustls 0.23.13",
|
||||
"slab",
|
||||
"thiserror",
|
||||
"tinyvec",
|
||||
|
@ -4814,15 +4815,15 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "quinn-udp"
|
||||
version = "0.5.4"
|
||||
version = "0.5.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8bffec3605b73c6f1754535084a85229fa8a30f86014e6c81aeec4abb68b0285"
|
||||
checksum = "4fe68c2e9e1a1234e218683dbdf9f9dfcb094113c5ac2b938dfcb9bab4c4140b"
|
||||
dependencies = [
|
||||
"libc",
|
||||
"once_cell",
|
||||
"socket2",
|
||||
"tracing",
|
||||
"windows-sys 0.52.0",
|
||||
"windows-sys 0.59.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
@ -5011,7 +5012,7 @@ dependencies = [
|
|||
"percent-encoding",
|
||||
"pin-project-lite",
|
||||
"rand",
|
||||
"rustls 0.23.12",
|
||||
"rustls 0.23.13",
|
||||
"rustls-native-certs 0.7.3",
|
||||
"rustls-pemfile 2.1.3",
|
||||
"rustls-pki-types",
|
||||
|
@ -5022,23 +5023,14 @@ dependencies = [
|
|||
"tokio-rustls 0.26.0",
|
||||
"tokio-util",
|
||||
"url",
|
||||
"webpki-roots 0.26.5",
|
||||
"webpki-roots 0.26.6",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "redox_syscall"
|
||||
version = "0.4.1"
|
||||
version = "0.5.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "4722d768eff46b75989dd134e5c353f0d6296e5aaa3132e776cbdb56be7731aa"
|
||||
dependencies = [
|
||||
"bitflags 1.3.2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "redox_syscall"
|
||||
version = "0.5.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2a908a6e00f1fdd0dfd9c0eb08ce85126f6d8bbda50017e74bc4a4b7d4a926a4"
|
||||
checksum = "0884ad60e090bf1345b93da0a5de8923c93884cd03f40dfcfddd3b4bee661853"
|
||||
dependencies = [
|
||||
"bitflags 2.6.0",
|
||||
]
|
||||
|
@ -5152,7 +5144,7 @@ dependencies = [
|
|||
"http-body 1.0.1",
|
||||
"http-body-util",
|
||||
"hyper 1.4.1",
|
||||
"hyper-rustls 0.27.2",
|
||||
"hyper-rustls 0.27.3",
|
||||
"hyper-util",
|
||||
"ipnet",
|
||||
"js-sys",
|
||||
|
@ -5163,7 +5155,7 @@ dependencies = [
|
|||
"percent-encoding",
|
||||
"pin-project-lite",
|
||||
"quinn",
|
||||
"rustls 0.23.12",
|
||||
"rustls 0.23.13",
|
||||
"rustls-pemfile 2.1.3",
|
||||
"rustls-pki-types",
|
||||
"serde",
|
||||
|
@ -5177,7 +5169,7 @@ dependencies = [
|
|||
"wasm-bindgen",
|
||||
"wasm-bindgen-futures",
|
||||
"web-sys",
|
||||
"webpki-roots 0.26.5",
|
||||
"webpki-roots 0.26.6",
|
||||
"windows-registry",
|
||||
]
|
||||
|
||||
|
@ -5480,9 +5472,9 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "rustix"
|
||||
version = "0.38.35"
|
||||
version = "0.38.37"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a85d50532239da68e9addb745ba38ff4612a242c1c7ceea689c4bc7c2f43c36f"
|
||||
checksum = "8acb788b847c24f28525660c4d7758620a7210875711f79e7f663cc152726811"
|
||||
dependencies = [
|
||||
"bitflags 2.6.0",
|
||||
"errno",
|
||||
|
@ -5512,21 +5504,21 @@ dependencies = [
|
|||
"log",
|
||||
"ring 0.17.8",
|
||||
"rustls-pki-types",
|
||||
"rustls-webpki 0.102.7",
|
||||
"rustls-webpki 0.102.8",
|
||||
"subtle",
|
||||
"zeroize",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rustls"
|
||||
version = "0.23.12"
|
||||
version = "0.23.13"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c58f8c84392efc0a126acce10fa59ff7b3d2ac06ab451a33f2741989b806b044"
|
||||
checksum = "f2dabaac7466917e566adb06783a81ca48944c6898a1b08b9374106dd671f4c8"
|
||||
dependencies = [
|
||||
"once_cell",
|
||||
"ring 0.17.8",
|
||||
"rustls-pki-types",
|
||||
"rustls-webpki 0.102.7",
|
||||
"rustls-webpki 0.102.8",
|
||||
"subtle",
|
||||
"zeroize",
|
||||
]
|
||||
|
@ -5593,9 +5585,9 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "rustls-webpki"
|
||||
version = "0.102.7"
|
||||
version = "0.102.8"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "84678086bd54edf2b415183ed7a94d0efb049f1b646a33e22a36f3794be6ae56"
|
||||
checksum = "64ca1bc8749bd4cf37b5ce386cc146580777b4e8572c7b97baf22c83f444bee9"
|
||||
dependencies = [
|
||||
"ring 0.17.8",
|
||||
"rustls-pki-types",
|
||||
|
@ -5640,20 +5632,20 @@ checksum = "ece8e78b2f38ec51c51f5d475df0a7187ba5111b2a28bdc761ee05b075d40a71"
|
|||
|
||||
[[package]]
|
||||
name = "scc"
|
||||
version = "2.1.16"
|
||||
version = "2.1.17"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "aeb7ac86243095b70a7920639507b71d51a63390d1ba26c4f60a552fbb914a37"
|
||||
checksum = "0c947adb109a8afce5fc9c7bf951f87f146e9147b3a6a58413105628fb1d1e66"
|
||||
dependencies = [
|
||||
"sdd",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "schannel"
|
||||
version = "0.1.23"
|
||||
version = "0.1.24"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "fbc91545643bcf3a0bbb6569265615222618bdf33ce4ffbbd13c4bbd4c093534"
|
||||
checksum = "e9aaafd5a2b6e3d657ff009d82fbd630b6bd54dd4eb06f21693925cdf80f9b8b"
|
||||
dependencies = [
|
||||
"windows-sys 0.52.0",
|
||||
"windows-sys 0.59.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
@ -5695,9 +5687,9 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "sdd"
|
||||
version = "3.0.2"
|
||||
version = "3.0.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0495e4577c672de8254beb68d01a9b62d0e8a13c099edecdbedccce3223cd29f"
|
||||
checksum = "60a7b59a5d9b0099720b417b6325d91a52cbf5b3dcb5041d864be53eefa58abc"
|
||||
|
||||
[[package]]
|
||||
name = "seahash"
|
||||
|
@ -5821,9 +5813,9 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "serde"
|
||||
version = "1.0.209"
|
||||
version = "1.0.210"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "99fce0ffe7310761ca6bf9faf5115afbc19688edd00171d81b1bb1b116c63e09"
|
||||
checksum = "c8e3592472072e6e22e0a54d5904d9febf8508f65fb8552499a1abc7d1078c3a"
|
||||
dependencies = [
|
||||
"serde_derive",
|
||||
]
|
||||
|
@ -5839,9 +5831,9 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "serde_derive"
|
||||
version = "1.0.209"
|
||||
version = "1.0.210"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a5831b979fd7b5439637af1752d535ff49f4860c0f341d1baeb6faf0f4242170"
|
||||
checksum = "243902eda00fad750862fc144cea25caca5e20d615af0a81bee94ca738f1df1f"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
|
@ -5850,9 +5842,9 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "serde_json"
|
||||
version = "1.0.127"
|
||||
version = "1.0.128"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8043c06d9f82bd7271361ed64f415fe5e12a77fdb52e573e7f06a516dea329ad"
|
||||
checksum = "6ff5456707a1de34e7e37f2a6fd3d3f808c318259cbd01ab6377795054b483d8"
|
||||
dependencies = [
|
||||
"itoa",
|
||||
"memchr",
|
||||
|
@ -6065,7 +6057,7 @@ checksum = "3c5e1a9a646d36c3599cd173a41282daf47c44583ad367b8e6837255952e5c67"
|
|||
|
||||
[[package]]
|
||||
name = "smtp"
|
||||
version = "0.9.4"
|
||||
version = "0.10.0"
|
||||
dependencies = [
|
||||
"ahash 0.8.11",
|
||||
"bincode",
|
||||
|
@ -6091,7 +6083,7 @@ dependencies = [
|
|||
"rayon",
|
||||
"regex",
|
||||
"reqwest 0.12.7",
|
||||
"rustls 0.23.12",
|
||||
"rustls 0.23.13",
|
||||
"rustls-pemfile 2.1.3",
|
||||
"rustls-pki-types",
|
||||
"serde",
|
||||
|
@ -6105,7 +6097,7 @@ dependencies = [
|
|||
"tokio-rustls 0.26.0",
|
||||
"trc",
|
||||
"utils",
|
||||
"webpki-roots 0.26.5",
|
||||
"webpki-roots 0.26.6",
|
||||
"x509-parser 0.16.0",
|
||||
]
|
||||
|
||||
|
@ -6181,7 +6173,7 @@ checksum = "a8f112729512f8e442d81f95a8a7ddf2b7c6b8a1a6f509a95864142b30cab2d3"
|
|||
|
||||
[[package]]
|
||||
name = "stalwart-cli"
|
||||
version = "0.9.4"
|
||||
version = "0.10.0"
|
||||
dependencies = [
|
||||
"clap",
|
||||
"console",
|
||||
|
@ -6212,7 +6204,7 @@ checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f"
|
|||
|
||||
[[package]]
|
||||
name = "store"
|
||||
version = "0.9.4"
|
||||
version = "0.10.0"
|
||||
dependencies = [
|
||||
"ahash 0.8.11",
|
||||
"arc-swap",
|
||||
|
@ -6244,7 +6236,7 @@ dependencies = [
|
|||
"rocksdb",
|
||||
"rusqlite",
|
||||
"rust-s3",
|
||||
"rustls 0.23.12",
|
||||
"rustls 0.23.13",
|
||||
"rustls-pki-types",
|
||||
"serde",
|
||||
"serde_json",
|
||||
|
@ -6462,7 +6454,7 @@ dependencies = [
|
|||
"rayon",
|
||||
"reqwest 0.12.7",
|
||||
"ring 0.17.8",
|
||||
"rustls 0.23.12",
|
||||
"rustls 0.23.13",
|
||||
"rustls-pemfile 2.1.3",
|
||||
"rustls-pki-types",
|
||||
"serde",
|
||||
|
@ -6604,9 +6596,9 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "tokio-postgres"
|
||||
version = "0.7.11"
|
||||
version = "0.7.12"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "03adcf0147e203b6032c0b2d30be1415ba03bc348901f3ff1cc0df6a733e60c3"
|
||||
checksum = "3b5d3742945bc7d7f210693b0c58ae542c6fd47b17adbbda0885f3dcb34a6bdb"
|
||||
dependencies = [
|
||||
"async-trait",
|
||||
"byteorder",
|
||||
|
@ -6655,16 +6647,16 @@ version = "0.26.0"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0c7bc40d0e5a97695bb96e27995cd3a08538541b0a846f65bba7a359f36700d4"
|
||||
dependencies = [
|
||||
"rustls 0.23.12",
|
||||
"rustls 0.23.13",
|
||||
"rustls-pki-types",
|
||||
"tokio",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "tokio-stream"
|
||||
version = "0.1.15"
|
||||
version = "0.1.16"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "267ac89e0bec6e691e5813911606935d77c476ff49024f98abcea3e7b15e37af"
|
||||
checksum = "4f4e6ce100d0eb49a2734f8c0812bcd324cf357d21810932c5df6b96ef2b86f1"
|
||||
dependencies = [
|
||||
"futures-core",
|
||||
"pin-project-lite",
|
||||
|
@ -6684,7 +6676,7 @@ dependencies = [
|
|||
"tokio",
|
||||
"tokio-rustls 0.25.0",
|
||||
"tungstenite 0.21.0",
|
||||
"webpki-roots 0.26.5",
|
||||
"webpki-roots 0.26.6",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
@ -6701,9 +6693,9 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "tokio-util"
|
||||
version = "0.7.11"
|
||||
version = "0.7.12"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9cf6b47b3771c49ac75ad09a6162f53ad4b8088b76ac60e8ec1455b31a189fe1"
|
||||
checksum = "61e7c3654c13bcd040d4a03abee2c75b1d14a37b423cf5a813ceae1cc903ec6a"
|
||||
dependencies = [
|
||||
"bytes",
|
||||
"futures-core",
|
||||
|
@ -6720,9 +6712,9 @@ checksum = "0dd7358ecb8fc2f8d014bf86f6f638ce72ba252a2c3a2572f2a795f1d23efb41"
|
|||
|
||||
[[package]]
|
||||
name = "toml_edit"
|
||||
version = "0.22.20"
|
||||
version = "0.22.21"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "583c44c02ad26b0c3f3066fe629275e50627026c51ac2e595cca4c230ce1ce1d"
|
||||
checksum = "3b072cee73c449a636ffd6f32bd8de3a9f7119139aff882f44943ce2986dc5cf"
|
||||
dependencies = [
|
||||
"indexmap 2.5.0",
|
||||
"toml_datetime",
|
||||
|
@ -6839,7 +6831,7 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "trc"
|
||||
version = "0.9.4"
|
||||
version = "0.10.0"
|
||||
dependencies = [
|
||||
"ahash 0.8.11",
|
||||
"base64 0.22.1",
|
||||
|
@ -6970,15 +6962,15 @@ checksum = "08f95100a766bf4f8f28f90d77e0a5461bbdb219042e7679bebe79004fed8d75"
|
|||
|
||||
[[package]]
|
||||
name = "unicode-ident"
|
||||
version = "1.0.12"
|
||||
version = "1.0.13"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b"
|
||||
checksum = "e91b56cd4cadaeb79bbf1a5645f6b4f8dc5bde8834ad5894a8db35fda9efa1fe"
|
||||
|
||||
[[package]]
|
||||
name = "unicode-normalization"
|
||||
version = "0.1.23"
|
||||
version = "0.1.24"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a56d1686db2308d901306f92a263857ef59ea39678a5458e7cb17f01415101f5"
|
||||
checksum = "5033c97c4262335cded6d6fc3e5c18ab755e1a3dc96376350f3d8e9f009ad956"
|
||||
dependencies = [
|
||||
"tinyvec",
|
||||
]
|
||||
|
@ -6997,9 +6989,9 @@ checksum = "ad8d71f5726e5f285a935e9fe8edfd53f0491eb6e9a5774097fdabee7cd8c9cd"
|
|||
|
||||
[[package]]
|
||||
name = "unicode-security"
|
||||
version = "0.1.1"
|
||||
version = "0.1.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ee9e13753df674873f3c4693b240ae5c03245ddc157dfccf7c26db9329af3a11"
|
||||
checksum = "2e4ddba1535dd35ed8b61c52166b7155d7f4e4b8847cec6f48e71dc66d8b5e50"
|
||||
dependencies = [
|
||||
"unicode-normalization",
|
||||
"unicode-script",
|
||||
|
@ -7013,9 +7005,9 @@ checksum = "0336d538f7abc86d282a4189614dfaa90810dfc2c6f6427eaf88e16311dd225d"
|
|||
|
||||
[[package]]
|
||||
name = "unicode-xid"
|
||||
version = "0.2.5"
|
||||
version = "0.2.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "229730647fbc343e3a80e463c1db7f78f3855d3f3739bee0dda773c9a037c90a"
|
||||
checksum = "ebc1c04c71510c7f702b52b7c350734c9ff1295c464a03335b00bb84fc54f853"
|
||||
|
||||
[[package]]
|
||||
name = "universal-hash"
|
||||
|
@ -7082,7 +7074,7 @@ checksum = "06abde3611657adf66d383f00b093d7faecc7fa57071cce2578660c9f1010821"
|
|||
|
||||
[[package]]
|
||||
name = "utils"
|
||||
version = "0.9.4"
|
||||
version = "0.10.0"
|
||||
dependencies = [
|
||||
"ahash 0.8.11",
|
||||
"base64 0.22.1",
|
||||
|
@ -7103,7 +7095,7 @@ dependencies = [
|
|||
"regex",
|
||||
"reqwest 0.12.7",
|
||||
"ring 0.17.8",
|
||||
"rustls 0.23.12",
|
||||
"rustls 0.23.13",
|
||||
"rustls-pemfile 2.1.3",
|
||||
"rustls-pki-types",
|
||||
"serde",
|
||||
|
@ -7112,7 +7104,7 @@ dependencies = [
|
|||
"tokio",
|
||||
"tokio-rustls 0.26.0",
|
||||
"trc",
|
||||
"webpki-roots 0.26.5",
|
||||
"webpki-roots 0.26.6",
|
||||
"x509-parser 0.16.0",
|
||||
]
|
||||
|
||||
|
@ -7282,9 +7274,9 @@ checksum = "5f20c57d8d7db6d3b86154206ae5d8fba62dd39573114de97c2cb0578251f8e1"
|
|||
|
||||
[[package]]
|
||||
name = "webpki-roots"
|
||||
version = "0.26.5"
|
||||
version = "0.26.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0bd24728e5af82c6c4ec1b66ac4844bdf8156257fccda846ec58b42cd0cdbe6a"
|
||||
checksum = "841c67bff177718f1d4dfefde8d8f0e78f9b6589319ba88312f567fc5841a958"
|
||||
dependencies = [
|
||||
"rustls-pki-types",
|
||||
]
|
||||
|
@ -7313,11 +7305,11 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "whoami"
|
||||
version = "1.5.1"
|
||||
version = "1.5.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a44ab49fad634e88f55bf8f9bb3abd2f27d7204172a112c7c9987e01c1c94ea9"
|
||||
checksum = "372d5b87f58ec45c384ba03563b03544dc5fadc3983e434b286913f5b4a9bb6d"
|
||||
dependencies = [
|
||||
"redox_syscall 0.4.1",
|
||||
"redox_syscall",
|
||||
"wasite",
|
||||
"web-sys",
|
||||
]
|
||||
|
@ -7643,9 +7635,9 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "xml-rs"
|
||||
version = "0.8.21"
|
||||
version = "0.8.22"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "539a77ee7c0de333dcc6da69b177380a0b81e0dacfa4f7344c465a36871ee601"
|
||||
checksum = "af4e2e2f7cba5a093896c1e150fbfe177d1883e7448200efb81d40b9d339ef26"
|
||||
|
||||
[[package]]
|
||||
name = "xxhash-rust"
|
||||
|
|
|
@ -5,7 +5,7 @@ authors = ["Stalwart Labs Ltd. <hello@stalw.art>"]
|
|||
license = "AGPL-3.0-only OR LicenseRef-SEL"
|
||||
repository = "https://github.com/stalwartlabs/cli"
|
||||
homepage = "https://github.com/stalwartlabs/cli"
|
||||
version = "0.9.4"
|
||||
version = "0.10.0"
|
||||
edition = "2021"
|
||||
readme = "README.md"
|
||||
resolver = "2"
|
||||
|
@ -29,4 +29,4 @@ human-size = "0.4.2"
|
|||
futures = "0.3.28"
|
||||
pwhash = "1.0.0"
|
||||
rand = "0.8.5"
|
||||
mail-auth = { version = "0.4" }
|
||||
mail-auth = { version = "0.5" }
|
||||
|
|
|
@ -88,7 +88,7 @@ async fn oauth(url: &str) -> Credentials {
|
|||
.danger_accept_invalid_certs(is_localhost(url))
|
||||
.build()
|
||||
.unwrap_or_default()
|
||||
.get(&format!("{}/.well-known/oauth-authorization-server", url))
|
||||
.get(format!("{}/.well-known/oauth-authorization-server", url))
|
||||
.send()
|
||||
.await
|
||||
.unwrap_result("send OAuth GET request")
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
[package]
|
||||
name = "common"
|
||||
version = "0.9.4"
|
||||
version = "0.10.0"
|
||||
edition = "2021"
|
||||
resolver = "2"
|
||||
|
||||
|
@ -14,7 +14,7 @@ jmap_proto = { path = "../jmap-proto" }
|
|||
sieve-rs = { version = "0.5" }
|
||||
mail-parser = { version = "0.9", features = ["full_encoding", "ludicrous_mode"] }
|
||||
mail-builder = { version = "0.3", features = ["ludicrous_mode"] }
|
||||
mail-auth = { version = "0.4" }
|
||||
mail-auth = { version = "0.5" }
|
||||
mail-send = { version = "0.4", default-features = false, features = ["cram-md5", "ring", "tls12"] }
|
||||
smtp-proto = { version = "0.1", features = ["serde_support"] }
|
||||
dns-update = { version = "0.1" }
|
||||
|
@ -58,6 +58,7 @@ hostname = "0.4.0"
|
|||
zip = "2.1"
|
||||
pwhash = "1.0.0"
|
||||
xxhash-rust = { version = "0.8.5", features = ["xxh3"] }
|
||||
psl = "2"
|
||||
|
||||
[target.'cfg(unix)'.dependencies]
|
||||
privdrop = "0.5.3"
|
||||
|
|
|
@ -175,6 +175,7 @@ impl Core {
|
|||
32,
|
||||
),
|
||||
permissions_version: Default::default(),
|
||||
logos: Default::default(),
|
||||
},
|
||||
storage: Storage {
|
||||
data,
|
||||
|
|
|
@ -22,10 +22,7 @@ use mail_auth::{
|
|||
Resolver,
|
||||
};
|
||||
use parking_lot::Mutex;
|
||||
use utils::{
|
||||
config::{utils::ParseValue, Config},
|
||||
suffixlist::PublicSuffix,
|
||||
};
|
||||
use utils::config::{utils::ParseValue, Config};
|
||||
|
||||
use crate::Core;
|
||||
|
||||
|
@ -33,7 +30,6 @@ pub struct Resolvers {
|
|||
pub dns: Resolver,
|
||||
pub dnssec: DnssecResolver,
|
||||
pub cache: DnsRecordCache,
|
||||
pub psl: PublicSuffix,
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
|
@ -237,7 +233,6 @@ impl Resolvers {
|
|||
.unwrap_or(1024),
|
||||
),
|
||||
},
|
||||
psl: PublicSuffix::parse(config, "resolver.public-suffix").await,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -356,7 +351,6 @@ impl Default for Resolvers {
|
|||
tlsa: LruCache::with_capacity(1024),
|
||||
mta_sts: LruCache::with_capacity(1024),
|
||||
},
|
||||
psl: PublicSuffix::default(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -402,7 +396,6 @@ impl Clone for Resolvers {
|
|||
dns: self.dns.clone(),
|
||||
dnssec: self.dnssec.clone(),
|
||||
cache: self.cache.clone(),
|
||||
psl: self.psl.clone(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -117,6 +117,7 @@ impl Enterprise {
|
|||
.property_or_default::<Option<Duration>>("storage.undelete.retention", "false")
|
||||
.unwrap_or_default()
|
||||
.map(|retention| Undelete { retention }),
|
||||
logo_url: config.value("enterprise.logo-url").map(|s| s.to_string()),
|
||||
trace_store,
|
||||
metrics_store,
|
||||
metrics_alerts: parse_metric_alerts(config),
|
||||
|
|
|
@ -15,17 +15,22 @@ pub mod undelete;
|
|||
|
||||
use std::time::Duration;
|
||||
|
||||
use directory::{
|
||||
backend::internal::{lookup::DirectoryStore, PrincipalField},
|
||||
QueryBy, Type,
|
||||
};
|
||||
use license::LicenseKey;
|
||||
use mail_parser::DateTime;
|
||||
use store::Store;
|
||||
use trc::{EventType, MetricType};
|
||||
use trc::{AddContext, EventType, MetricType};
|
||||
use utils::config::cron::SimpleCron;
|
||||
|
||||
use crate::{expr::Expression, Core};
|
||||
use crate::{expr::Expression, manager::webadmin::Resource, Core};
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct Enterprise {
|
||||
pub license: LicenseKey,
|
||||
pub logo_url: Option<String>,
|
||||
pub undelete: Option<Undelete>,
|
||||
pub trace_store: Option<TraceStore>,
|
||||
pub metrics_store: Option<MetricStore>,
|
||||
|
@ -114,4 +119,95 @@ impl Core {
|
|||
);
|
||||
}
|
||||
}
|
||||
|
||||
pub async fn logo_resource(&self, domain: &str) -> trc::Result<Option<Resource<Vec<u8>>>> {
|
||||
if self.is_enterprise_edition() {
|
||||
let domain = psl::domain_str(domain).unwrap_or(domain);
|
||||
let logo = { self.security.logos.lock().get(domain).cloned() };
|
||||
|
||||
if let Some(logo) = logo {
|
||||
Ok(logo)
|
||||
} else {
|
||||
// Try fetching the logo for the domain
|
||||
let logo_url = if let Some(mut principal) = self
|
||||
.storage
|
||||
.data
|
||||
.query(QueryBy::Name(domain), false)
|
||||
.await
|
||||
.caused_by(trc::location!())?
|
||||
.filter(|p| p.typ() == Type::Domain)
|
||||
{
|
||||
if let Some(logo) = principal
|
||||
.take_str(PrincipalField::Picture)
|
||||
.filter(|l| l.starts_with("http"))
|
||||
{
|
||||
logo.into()
|
||||
} else if let Some(tenant_id) = principal.get_int(PrincipalField::Tenant) {
|
||||
if let Some(logo) = self
|
||||
.storage
|
||||
.data
|
||||
.query(QueryBy::Id(tenant_id as u32), false)
|
||||
.await
|
||||
.caused_by(trc::location!())?
|
||||
.and_then(|mut p| p.take_str(PrincipalField::Picture))
|
||||
.filter(|l| l.starts_with("http"))
|
||||
{
|
||||
logo.into()
|
||||
} else {
|
||||
self.default_logo_url()
|
||||
}
|
||||
} else {
|
||||
self.default_logo_url()
|
||||
}
|
||||
} else {
|
||||
self.default_logo_url()
|
||||
};
|
||||
|
||||
let mut logo = None;
|
||||
if let Some(logo_url) = logo_url {
|
||||
let response = reqwest::get(&logo_url).await.map_err(|err| {
|
||||
trc::ResourceEvent::DownloadExternal
|
||||
.into_err()
|
||||
.details("Failed to download logo")
|
||||
.reason(err)
|
||||
})?;
|
||||
|
||||
let content_type = response
|
||||
.headers()
|
||||
.get(reqwest::header::CONTENT_TYPE)
|
||||
.and_then(|ct| ct.to_str().ok())
|
||||
.unwrap_or("image/svg+xml")
|
||||
.to_string();
|
||||
|
||||
let contents = response
|
||||
.bytes()
|
||||
.await
|
||||
.map_err(|err| {
|
||||
trc::ResourceEvent::DownloadExternal
|
||||
.into_err()
|
||||
.details("Failed to download logo")
|
||||
.reason(err)
|
||||
})?
|
||||
.to_vec();
|
||||
|
||||
logo = Resource::new(content_type, contents).into();
|
||||
}
|
||||
|
||||
self.security
|
||||
.logos
|
||||
.lock()
|
||||
.insert(domain.to_string(), logo.clone());
|
||||
|
||||
Ok(logo)
|
||||
}
|
||||
} else {
|
||||
Ok(None)
|
||||
}
|
||||
}
|
||||
|
||||
fn default_logo_url(&self) -> Option<String> {
|
||||
self.enterprise
|
||||
.as_ref()
|
||||
.and_then(|e| e.logo_url.as_ref().map(|l| l.to_string()))
|
||||
}
|
||||
}
|
||||
|
|
|
@ -91,14 +91,14 @@ impl<'x> Tokenizer<'x> {
|
|||
_ => {
|
||||
let (prev_token, ch) = if ch == b'(' && self.buf.eq(b"matches") {
|
||||
// Parse regular expressions
|
||||
let stop_ch = self.find_char(&[b'\"', b'\''])?;
|
||||
let stop_ch = self.find_char(b"\"'")?;
|
||||
let regex_str = self.parse_string(stop_ch)?;
|
||||
let regex = Regex::new(®ex_str).map_err(|e| {
|
||||
format!("Invalid regular expression {:?}: {}", regex_str, e)
|
||||
})?;
|
||||
self.has_alpha = false;
|
||||
self.buf.clear();
|
||||
self.find_char(&[b','])?;
|
||||
self.find_char(b",")?;
|
||||
(Token::Regex(regex).into(), b'(')
|
||||
} else if !self.buf.is_empty() {
|
||||
self.is_start = false;
|
||||
|
|
|
@ -10,6 +10,7 @@ use std::{
|
|||
sync::{atomic::AtomicU8, Arc},
|
||||
};
|
||||
|
||||
use ahash::AHashMap;
|
||||
use arc_swap::ArcSwap;
|
||||
use auth::{roles::RolePermissions, AccessToken};
|
||||
use config::{
|
||||
|
@ -35,6 +36,8 @@ use listener::{
|
|||
};
|
||||
use mail_send::Credentials;
|
||||
|
||||
use manager::webadmin::Resource;
|
||||
use parking_lot::Mutex;
|
||||
use sieve::Sieve;
|
||||
use store::{
|
||||
write::{QueueClass, ValueClass},
|
||||
|
@ -58,6 +61,8 @@ pub mod manager;
|
|||
pub mod scripts;
|
||||
pub mod telemetry;
|
||||
|
||||
pub use psl;
|
||||
|
||||
pub static USER_AGENT: &str = concat!("Stalwart/", env!("CARGO_PKG_VERSION"),);
|
||||
pub static DAEMON_NAME: &str = concat!("Stalwart Mail Server v", env!("CARGO_PKG_VERSION"),);
|
||||
|
||||
|
@ -83,6 +88,7 @@ pub struct Core {
|
|||
//TODO: temporary hack until OIDC is implemented
|
||||
#[derive(Default)]
|
||||
pub struct Security {
|
||||
pub logos: Mutex<AHashMap<String, Option<Resource<Vec<u8>>>>>,
|
||||
pub access_tokens: TtlDashMap<u32, Arc<AccessToken>>,
|
||||
pub permissions: ADashMap<u32, Arc<RolePermissions>>,
|
||||
pub permissions_version: AtomicU8,
|
||||
|
@ -405,6 +411,7 @@ impl Clone for Security {
|
|||
self.permissions_version
|
||||
.load(std::sync::atomic::Ordering::Relaxed),
|
||||
),
|
||||
logos: Mutex::new(self.logos.lock().clone()),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -9,7 +9,6 @@ use rustls::sign::CertifiedKey;
|
|||
use rustls_pki_types::{CertificateDer, PrivateKeyDer, PrivatePkcs8KeyDer};
|
||||
use std::sync::Arc;
|
||||
use std::time::{Duration, Instant};
|
||||
use utils::suffixlist::DomainPart;
|
||||
use x509_parser::parse_x509_certificate;
|
||||
|
||||
use crate::listener::acme::directory::Identifier;
|
||||
|
@ -241,11 +240,10 @@ impl Core {
|
|||
let domain = domain.strip_prefix("*.").unwrap_or(&domain);
|
||||
let name = format!("_acme-challenge.{}", domain);
|
||||
let origin = origin
|
||||
.clone()
|
||||
.or_else(|| {
|
||||
self.smtp.resolvers.psl.domain_part(domain, DomainPart::Sld)
|
||||
})
|
||||
.unwrap_or_else(|| domain.to_string());
|
||||
.as_deref()
|
||||
.or_else(|| psl::domain_str(domain))
|
||||
.unwrap_or(domain)
|
||||
.to_string();
|
||||
|
||||
// First try deleting the record
|
||||
if let Err(err) = updater.delete(&name, &origin).await {
|
||||
|
|
|
@ -5,6 +5,7 @@
|
|||
*/
|
||||
|
||||
use std::{
|
||||
borrow::Cow,
|
||||
io::{self, Cursor, Read},
|
||||
path::PathBuf,
|
||||
};
|
||||
|
@ -22,12 +23,21 @@ pub struct WebAdminManager {
|
|||
routes: ArcSwap<AHashMap<String, Resource<PathBuf>>>,
|
||||
}
|
||||
|
||||
#[derive(Default)]
|
||||
#[derive(Default, Clone)]
|
||||
pub struct Resource<T> {
|
||||
pub content_type: &'static str,
|
||||
pub content_type: Cow<'static, str>,
|
||||
pub contents: T,
|
||||
}
|
||||
|
||||
impl<T> Resource<T> {
|
||||
pub fn new(content_type: impl Into<Cow<'static, str>>, contents: T) -> Self {
|
||||
Self {
|
||||
content_type: content_type.into(),
|
||||
contents,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl WebAdminManager {
|
||||
pub fn new() -> Self {
|
||||
Self {
|
||||
|
@ -42,7 +52,7 @@ impl WebAdminManager {
|
|||
tokio::fs::read(&resource.contents)
|
||||
.await
|
||||
.map(|contents| Resource {
|
||||
content_type: resource.content_type,
|
||||
content_type: resource.content_type.clone(),
|
||||
contents,
|
||||
})
|
||||
.map_err(|err| {
|
||||
|
@ -114,7 +124,8 @@ impl WebAdminManager {
|
|||
"svg" => "image/svg+xml",
|
||||
"ico" => "image/x-icon",
|
||||
_ => "application/octet-stream",
|
||||
},
|
||||
}
|
||||
.into(),
|
||||
contents: path,
|
||||
};
|
||||
|
||||
|
|
|
@ -9,10 +9,10 @@ use unicode_security::MixedScript;
|
|||
|
||||
pub fn fn_is_ascii<'x>(_: &'x Context<'x>, v: Vec<Variable>) -> Variable {
|
||||
match &v[0] {
|
||||
Variable::String(s) => s.chars().all(|c| c.is_ascii()),
|
||||
Variable::String(s) => s.is_ascii(),
|
||||
Variable::Integer(_) | Variable::Float(_) => true,
|
||||
Variable::Array(a) => a.iter().all(|v| match v {
|
||||
Variable::String(s) => s.chars().all(|c| c.is_ascii()),
|
||||
Variable::String(s) => s.is_ascii(),
|
||||
_ => true,
|
||||
}),
|
||||
}
|
||||
|
|
|
@ -63,10 +63,7 @@ async fn train(ctx: PluginContext<'_>, is_train: bool) -> trc::Result<Variable>
|
|||
// Train the model
|
||||
let mut model = BayesModel::default();
|
||||
model.train(
|
||||
OsbTokenizer::new(
|
||||
BayesTokenizer::new(text.as_ref(), &ctx.core.smtp.resolvers.psl),
|
||||
5,
|
||||
),
|
||||
OsbTokenizer::new(BayesTokenizer::new(text.as_ref()), 5),
|
||||
is_spam,
|
||||
);
|
||||
if model.weights.is_empty() {
|
||||
|
@ -187,10 +184,7 @@ pub async fn exec_classify(ctx: PluginContext<'_>) -> trc::Result<Variable> {
|
|||
|
||||
// Classify the text
|
||||
let mut tokens = Vec::new();
|
||||
for token in OsbTokenizer::<_, TokenHash>::new(
|
||||
BayesTokenizer::new(text.as_ref(), &ctx.core.smtp.resolvers.psl),
|
||||
5,
|
||||
) {
|
||||
for token in OsbTokenizer::<_, TokenHash>::new(BayesTokenizer::new(text.as_ref()), 5) {
|
||||
let weights = bayes_cache.get_or_update(token.inner, store).await?;
|
||||
tokens.push(OsbToken {
|
||||
inner: weights,
|
||||
|
|
|
@ -18,7 +18,6 @@ use mail_parser::{decoders::html::add_html_token, Message, PartType};
|
|||
use nlp::tokenizers::types::{TokenType, TypesTokenizer};
|
||||
use sha1::{Digest, Sha1};
|
||||
use tokio::net::UdpSocket;
|
||||
use utils::suffixlist::PublicSuffix;
|
||||
|
||||
const MIN_LINE_LENGTH: usize = 8;
|
||||
const ATOMIC_NUM_LINES: usize = 4;
|
||||
|
@ -47,9 +46,7 @@ pub async fn exec(ctx: PluginContext<'_>) -> trc::Result<Variable> {
|
|||
}
|
||||
|
||||
// Hash message
|
||||
let request = ctx
|
||||
.message
|
||||
.pyzor_check_message(&ctx.core.smtp.resolvers.psl);
|
||||
let request = ctx.message.pyzor_check_message();
|
||||
|
||||
#[cfg(feature = "test_mode")]
|
||||
{
|
||||
|
@ -161,15 +158,15 @@ async fn pyzor_send_message(
|
|||
}
|
||||
|
||||
trait PyzorDigest<W: Write> {
|
||||
fn pyzor_digest(&self, writer: W, psl: &PublicSuffix) -> W;
|
||||
fn pyzor_digest(&self, writer: W) -> W;
|
||||
}
|
||||
|
||||
pub trait PyzorCheck {
|
||||
fn pyzor_check_message(&self, psl: &PublicSuffix) -> String;
|
||||
fn pyzor_check_message(&self) -> String;
|
||||
}
|
||||
|
||||
impl<'x, W: Write> PyzorDigest<W> for Message<'x> {
|
||||
fn pyzor_digest(&self, writer: W, psl: &PublicSuffix) -> W {
|
||||
fn pyzor_digest(&self, writer: W) -> W {
|
||||
let parts = self
|
||||
.parts
|
||||
.iter()
|
||||
|
@ -180,33 +177,27 @@ impl<'x, W: Write> PyzorDigest<W> for Message<'x> {
|
|||
})
|
||||
.collect::<Vec<Cow<str>>>();
|
||||
|
||||
pyzor_digest(writer, parts.iter().flat_map(|text| text.lines()), psl)
|
||||
pyzor_digest(writer, parts.iter().flat_map(|text| text.lines()))
|
||||
}
|
||||
}
|
||||
|
||||
impl<'x> PyzorCheck for Message<'x> {
|
||||
fn pyzor_check_message(&self, psl: &PublicSuffix) -> String {
|
||||
fn pyzor_check_message(&self) -> String {
|
||||
let time = SystemTime::now()
|
||||
.duration_since(SystemTime::UNIX_EPOCH)
|
||||
.map_or(0, |d| d.as_secs());
|
||||
|
||||
pyzor_create_message(
|
||||
self,
|
||||
psl,
|
||||
time,
|
||||
(time & 0xFFFF) as u16 ^ ((time >> 16) & 0xFFFF) as u16,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
fn pyzor_create_message(
|
||||
message: &Message<'_>,
|
||||
psl: &PublicSuffix,
|
||||
time: u64,
|
||||
thread: u16,
|
||||
) -> String {
|
||||
fn pyzor_create_message(message: &Message<'_>, time: u64, thread: u16) -> String {
|
||||
// Hash message
|
||||
let hash = message.pyzor_digest(Sha1::new(), psl).finalize();
|
||||
let hash = message.pyzor_digest(Sha1::new()).finalize();
|
||||
// Hash key
|
||||
let mut hash_key = Sha1::new();
|
||||
hash_key.update("anonymous:".as_bytes());
|
||||
|
@ -223,13 +214,13 @@ fn pyzor_create_message(
|
|||
// Sign
|
||||
let mut sig = Sha1::new();
|
||||
sig.update(msg_hash);
|
||||
sig.update(&format!(":{time}:{hash_key:x}"));
|
||||
sig.update(format!(":{time}:{hash_key:x}"));
|
||||
let sig = sig.finalize();
|
||||
|
||||
format!("{message}\nSig: {sig:x}\n")
|
||||
}
|
||||
|
||||
fn pyzor_digest<'x, I, W>(mut writer: W, lines: I, psl: &PublicSuffix) -> W
|
||||
fn pyzor_digest<'x, I, W>(mut writer: W, lines: I) -> W
|
||||
where
|
||||
I: Iterator<Item = &'x str>,
|
||||
W: Write,
|
||||
|
@ -254,7 +245,7 @@ where
|
|||
}
|
||||
};
|
||||
|
||||
for token in TypesTokenizer::new(line, psl) {
|
||||
for token in TypesTokenizer::new(line) {
|
||||
match token.word {
|
||||
TokenType::Alphabetic(_)
|
||||
| TokenType::Alphanumeric(_)
|
||||
|
@ -448,7 +439,6 @@ mod test {
|
|||
use mail_parser::MessageParser;
|
||||
use sha1::Digest;
|
||||
use sha1::Sha1;
|
||||
use utils::suffixlist::PublicSuffix;
|
||||
|
||||
use super::pyzor_create_message;
|
||||
use super::pyzor_send_message;
|
||||
|
@ -485,11 +475,8 @@ mod test {
|
|||
|
||||
#[test]
|
||||
fn message_pyzor() {
|
||||
let mut psl = PublicSuffix::default();
|
||||
psl.suffixes.insert("com".to_string());
|
||||
let message = pyzor_create_message(
|
||||
&MessageParser::new().parse(HTML_TEXT_STYLE_SCRIPT).unwrap(),
|
||||
&psl,
|
||||
1697468672,
|
||||
49005,
|
||||
);
|
||||
|
@ -510,9 +497,6 @@ mod test {
|
|||
|
||||
#[test]
|
||||
fn digest_pyzor() {
|
||||
let mut psl = PublicSuffix::default();
|
||||
psl.suffixes.insert("com".to_string());
|
||||
|
||||
// HTML stripping
|
||||
assert_eq!(html_to_text(HTML_RAW), HTML_RAW_STRIPED);
|
||||
|
||||
|
@ -531,7 +515,6 @@ mod test {
|
|||
String::from_utf8(pyzor_digest(
|
||||
Vec::new(),
|
||||
format!("Test {strip_me} Test2").lines(),
|
||||
&psl
|
||||
))
|
||||
.unwrap(),
|
||||
"TestTest2"
|
||||
|
@ -543,7 +526,6 @@ mod test {
|
|||
String::from_utf8(pyzor_digest(
|
||||
Vec::new(),
|
||||
concat!("This line is included\n", "not this\n", "This also").lines(),
|
||||
&psl
|
||||
))
|
||||
.unwrap(),
|
||||
"ThislineisincludedThisalso"
|
||||
|
@ -554,7 +536,6 @@ mod test {
|
|||
String::from_utf8(pyzor_digest(
|
||||
Vec::new(),
|
||||
"All this message\nShould be included\nIn the digest".lines(),
|
||||
&psl
|
||||
))
|
||||
.unwrap(),
|
||||
"AllthismessageShouldbeincludedInthedigest"
|
||||
|
@ -570,7 +551,7 @@ mod test {
|
|||
expected += format!("Line{i}testtesttest").as_str();
|
||||
}
|
||||
assert_eq!(
|
||||
String::from_utf8(pyzor_digest(Vec::new(), text.lines(), &psl)).unwrap(),
|
||||
String::from_utf8(pyzor_digest(Vec::new(), text.lines(),)).unwrap(),
|
||||
expected
|
||||
);
|
||||
|
||||
|
@ -602,7 +583,7 @@ mod test {
|
|||
MessageParser::new()
|
||||
.parse(input)
|
||||
.unwrap()
|
||||
.pyzor_digest(Vec::new(), &psl)
|
||||
.pyzor_digest(Vec::new(),)
|
||||
)
|
||||
.unwrap(),
|
||||
expected,
|
||||
|
@ -617,7 +598,7 @@ mod test {
|
|||
MessageParser::new()
|
||||
.parse(HTML_TEXT_STYLE_SCRIPT)
|
||||
.unwrap()
|
||||
.pyzor_digest(Sha1::new(), &psl)
|
||||
.pyzor_digest(Sha1::new(),)
|
||||
.finalize()
|
||||
),
|
||||
"b2c27325a034c581df0c9ef37e4a0d63208a3e7e",
|
||||
|
|
|
@ -6,7 +6,6 @@
|
|||
|
||||
use nlp::tokenizers::types::{TokenType, TypesTokenizer};
|
||||
use sieve::{runtime::Variable, FunctionMap};
|
||||
use utils::suffixlist::DomainPart;
|
||||
|
||||
use crate::scripts::functions::{html::html_to_tokens, text::tokenize_words, ApplyString};
|
||||
|
||||
|
@ -33,7 +32,7 @@ pub fn exec_tokenize(ctx: PluginContext<'_>) -> trc::Result<Variable> {
|
|||
|
||||
Ok(match v.remove(0) {
|
||||
v @ (Variable::String(_) | Variable::Array(_)) => {
|
||||
TypesTokenizer::new(v.to_string().as_ref(), &ctx.core.smtp.resolvers.psl)
|
||||
TypesTokenizer::new(v.to_string().as_ref())
|
||||
.tokenize_numbers(false)
|
||||
.tokenize_urls(urls)
|
||||
.tokenize_urls_without_scheme(urls_without_scheme)
|
||||
|
@ -53,6 +52,12 @@ pub fn exec_tokenize(ctx: PluginContext<'_>) -> trc::Result<Variable> {
|
|||
})
|
||||
}
|
||||
|
||||
enum DomainPart {
|
||||
Sld,
|
||||
Tld,
|
||||
Host,
|
||||
}
|
||||
|
||||
pub fn exec_domain_part(ctx: PluginContext<'_>) -> trc::Result<Variable> {
|
||||
let v = ctx.arguments;
|
||||
let part = match v[1].to_string().as_ref() {
|
||||
|
@ -63,11 +68,11 @@ pub fn exec_domain_part(ctx: PluginContext<'_>) -> trc::Result<Variable> {
|
|||
};
|
||||
|
||||
Ok(v[0].transform(|domain| {
|
||||
ctx.core
|
||||
.smtp
|
||||
.resolvers
|
||||
.psl
|
||||
.domain_part(domain, part)
|
||||
match part {
|
||||
DomainPart::Sld => psl::domain_str(domain),
|
||||
DomainPart::Tld => domain.rsplit_once('.').map(|(_, tld)| tld),
|
||||
DomainPart::Host => domain.split_once('.').map(|(host, _)| host),
|
||||
}
|
||||
.map(Variable::from)
|
||||
.unwrap_or_default()
|
||||
}))
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
[package]
|
||||
name = "directory"
|
||||
version = "0.9.4"
|
||||
version = "0.10.0"
|
||||
edition = "2021"
|
||||
resolver = "2"
|
||||
|
||||
|
|
|
@ -405,7 +405,7 @@ impl ManageDirectory for Store {
|
|||
principal_id: MaybeDynamicId::Static(member.id),
|
||||
member_of: MaybeDynamicId::Dynamic(0),
|
||||
}),
|
||||
vec![member.typ as u8],
|
||||
vec![principal.typ as u8],
|
||||
);
|
||||
batch.set(
|
||||
ValueClass::Directory(DirectoryClass::Members {
|
||||
|
@ -460,7 +460,15 @@ impl ManageDirectory for Store {
|
|||
.list_principals(
|
||||
None,
|
||||
principal.id().into(),
|
||||
&[],
|
||||
&[
|
||||
Type::Individual,
|
||||
Type::Group,
|
||||
Type::Role,
|
||||
Type::List,
|
||||
Type::Resource,
|
||||
Type::Other,
|
||||
Type::Location,
|
||||
],
|
||||
&[PrincipalField::Name],
|
||||
0,
|
||||
0,
|
||||
|
@ -742,13 +750,16 @@ impl ManageDirectory for Store {
|
|||
));
|
||||
}
|
||||
|
||||
match principal.inner.tenant() {
|
||||
Some(old_tenant_id) if old_tenant_id != tenant_info.id => {
|
||||
if principal.inner.tenant() == Some(tenant_info.id) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// Update quota
|
||||
if let Some(used_quota) = used_quota {
|
||||
batch
|
||||
.add(DirectoryClass::UsedQuota(old_tenant_id), -used_quota)
|
||||
.add(DirectoryClass::UsedQuota(tenant_info.id), used_quota);
|
||||
if let Some(old_tenant_id) = principal.inner.tenant() {
|
||||
batch.add(DirectoryClass::UsedQuota(old_tenant_id), -used_quota);
|
||||
}
|
||||
batch.add(DirectoryClass::UsedQuota(tenant_info.id), used_quota);
|
||||
}
|
||||
|
||||
principal.inner.set(PrincipalField::Tenant, tenant_info.id);
|
||||
|
@ -758,9 +769,6 @@ impl ManageDirectory for Store {
|
|||
tenant_info.id.into(),
|
||||
)
|
||||
.serialize();
|
||||
}
|
||||
_ => continue,
|
||||
}
|
||||
} else if let Some(tenant_id) = principal.inner.tenant() {
|
||||
// Update quota
|
||||
if let Some(used_quota) = used_quota {
|
||||
|
@ -826,15 +834,13 @@ impl ManageDirectory for Store {
|
|||
}
|
||||
(
|
||||
PrincipalAction::Set,
|
||||
PrincipalField::Description,
|
||||
PrincipalValue::String(description),
|
||||
PrincipalField::Description | PrincipalField::Picture,
|
||||
PrincipalValue::String(value),
|
||||
) => {
|
||||
if !description.is_empty() {
|
||||
principal
|
||||
.inner
|
||||
.set(PrincipalField::Description, description);
|
||||
if !value.is_empty() {
|
||||
principal.inner.set(change.field, value);
|
||||
} else {
|
||||
principal.inner.remove(PrincipalField::Description);
|
||||
principal.inner.remove(change.field);
|
||||
}
|
||||
}
|
||||
(PrincipalAction::Set, PrincipalField::Quota, PrincipalValue::Integer(quota))
|
||||
|
@ -1118,7 +1124,7 @@ impl ManageDirectory for Store {
|
|||
principal_id: MaybeDynamicId::Static(member_info.id),
|
||||
member_of: MaybeDynamicId::Static(principal_id),
|
||||
}),
|
||||
vec![member_info.typ as u8],
|
||||
vec![principal.inner.typ as u8],
|
||||
);
|
||||
batch.set(
|
||||
ValueClass::Directory(DirectoryClass::Members {
|
||||
|
@ -1180,7 +1186,7 @@ impl ManageDirectory for Store {
|
|||
principal_id: MaybeDynamicId::Static(member_info.id),
|
||||
member_of: MaybeDynamicId::Static(principal_id),
|
||||
}),
|
||||
vec![member_info.typ as u8],
|
||||
vec![principal.inner.typ as u8],
|
||||
);
|
||||
batch.set(
|
||||
ValueClass::Directory(DirectoryClass::Members {
|
||||
|
@ -1561,25 +1567,24 @@ impl ManageDirectory for Store {
|
|||
let to_key = ValueKey::from(ValueClass::Directory(DirectoryClass::EmailToId(
|
||||
vec![u8::MAX; 10],
|
||||
)));
|
||||
let mut results = Vec::new();
|
||||
let domain_name = principal.name();
|
||||
let mut total: u64 = 0;
|
||||
self.iterate(
|
||||
IterateParams::new(from_key, to_key).no_values(),
|
||||
|key, _| {
|
||||
let email = std::str::from_utf8(key.get(1..).unwrap_or_default())
|
||||
.unwrap_or_default();
|
||||
if email
|
||||
if std::str::from_utf8(key.get(1..).unwrap_or_default())
|
||||
.unwrap_or_default()
|
||||
.rsplit_once('@')
|
||||
.map_or(false, |(_, domain)| domain == domain_name)
|
||||
{
|
||||
results.push(email.to_string());
|
||||
total += 1;
|
||||
}
|
||||
Ok(true)
|
||||
},
|
||||
)
|
||||
.await
|
||||
.caused_by(trc::location!())?;
|
||||
principal.set(PrincipalField::Members, results);
|
||||
principal.set(PrincipalField::Members, total);
|
||||
}
|
||||
Type::Tenant => {
|
||||
let from_key =
|
||||
|
@ -1587,25 +1592,23 @@ impl ManageDirectory for Store {
|
|||
let to_key = ValueKey::from(ValueClass::Directory(DirectoryClass::NameToId(
|
||||
vec![u8::MAX; 10],
|
||||
)));
|
||||
let mut results = Vec::new();
|
||||
self.iterate(IterateParams::new(from_key, to_key), |key, value| {
|
||||
let mut total: u64 = 0;
|
||||
|
||||
self.iterate(IterateParams::new(from_key, to_key), |_, value| {
|
||||
let pinfo =
|
||||
PrincipalInfo::deserialize(value).caused_by(trc::location!())?;
|
||||
|
||||
if pinfo.typ == Type::Individual
|
||||
&& pinfo.has_tenant_access(Some(principal.id))
|
||||
{
|
||||
results.push(
|
||||
std::str::from_utf8(key.get(1..).unwrap_or_default())
|
||||
.unwrap_or_default()
|
||||
.to_string(),
|
||||
);
|
||||
total += 1;
|
||||
}
|
||||
Ok(true)
|
||||
})
|
||||
.await
|
||||
.caused_by(trc::location!())?;
|
||||
principal.set(PrincipalField::Members, results);
|
||||
|
||||
principal.set(PrincipalField::Members, total);
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
|
|
|
@ -95,6 +95,7 @@ impl PrincipalInfo {
|
|||
pub fn has_tenant_access(&self, tenant_id: Option<u32>) -> bool {
|
||||
tenant_id.map_or(true, |tenant_id| {
|
||||
self.tenant.map_or(false, |t| tenant_id == t)
|
||||
|| (self.typ == Type::Tenant && self.id == tenant_id)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
[package]
|
||||
name = "imap"
|
||||
version = "0.9.4"
|
||||
version = "0.10.0"
|
||||
edition = "2021"
|
||||
resolver = "2"
|
||||
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
[package]
|
||||
name = "jmap"
|
||||
version = "0.9.4"
|
||||
version = "0.10.0"
|
||||
edition = "2021"
|
||||
resolver = "2"
|
||||
|
||||
|
@ -17,7 +17,7 @@ smtp-proto = { version = "0.1" }
|
|||
mail-parser = { version = "0.9", features = ["full_encoding", "serde_support", "ludicrous_mode"] }
|
||||
mail-builder = { version = "0.3", features = ["ludicrous_mode"] }
|
||||
mail-send = { version = "0.4", default-features = false, features = ["cram-md5", "ring", "tls12"] }
|
||||
mail-auth = { version = "0.4", features = ["generate"] }
|
||||
mail-auth = { version = "0.5", features = ["generate"] }
|
||||
sieve-rs = { version = "0.5" }
|
||||
serde = { version = "1.0", features = ["derive"]}
|
||||
serde_json = "1.0"
|
||||
|
|
|
@ -67,11 +67,10 @@ impl JMAP {
|
|||
);
|
||||
config.push_str("</clientConfig>\n");
|
||||
|
||||
Ok(Resource {
|
||||
content_type: "application/xml; charset=utf-8",
|
||||
contents: config.into_bytes(),
|
||||
}
|
||||
.into_http_response())
|
||||
Ok(
|
||||
Resource::new("application/xml; charset=utf-8", config.into_bytes())
|
||||
.into_http_response(),
|
||||
)
|
||||
}
|
||||
|
||||
pub async fn handle_autodiscover_request(
|
||||
|
@ -147,11 +146,10 @@ impl JMAP {
|
|||
let _ = writeln!(&mut config, "\t</Response>");
|
||||
let _ = writeln!(&mut config, "</Autodiscover>");
|
||||
|
||||
Ok(Resource {
|
||||
content_type: "application/xml; charset=utf-8",
|
||||
contents: config.into_bytes(),
|
||||
}
|
||||
.into_http_response())
|
||||
Ok(
|
||||
Resource::new("application/xml; charset=utf-8", config.into_bytes())
|
||||
.into_http_response(),
|
||||
)
|
||||
}
|
||||
|
||||
async fn autoconfig_parameters<'x>(
|
||||
|
|
|
@ -223,10 +223,7 @@ impl JMAP {
|
|||
.key_get::<String>(format!("acme:{token}").into_bytes())
|
||||
.await?
|
||||
{
|
||||
Some(proof) => Ok(Resource {
|
||||
content_type: "text/plain",
|
||||
contents: proof.into_bytes(),
|
||||
}
|
||||
Some(proof) => Ok(Resource::new("text/plain", proof.into_bytes())
|
||||
.into_http_response()),
|
||||
None => Err(trc::ResourceEvent::NotFound.into_err()),
|
||||
};
|
||||
|
@ -234,10 +231,7 @@ impl JMAP {
|
|||
}
|
||||
("mta-sts.txt", &Method::GET) => {
|
||||
if let Some(policy) = self.core.build_mta_sts_policy() {
|
||||
return Ok(Resource {
|
||||
content_type: "text/plain",
|
||||
contents: policy.to_string().into_bytes(),
|
||||
}
|
||||
return Ok(Resource::new("text/plain", policy.to_string().into_bytes())
|
||||
.into_http_response());
|
||||
} else {
|
||||
return Err(trc::ResourceEvent::NotFound.into_err());
|
||||
|
@ -357,11 +351,10 @@ impl JMAP {
|
|||
}
|
||||
}
|
||||
"robots.txt" => {
|
||||
return Ok(Resource {
|
||||
content_type: "text/plain",
|
||||
contents: b"User-agent: *\nDisallow: /\n".to_vec(),
|
||||
}
|
||||
.into_http_response());
|
||||
return Ok(
|
||||
Resource::new("text/plain", b"User-agent: *\nDisallow: /\n".to_vec())
|
||||
.into_http_response(),
|
||||
);
|
||||
}
|
||||
"healthz" => match path.next().unwrap_or_default() {
|
||||
"live" => {
|
||||
|
@ -394,10 +387,10 @@ impl JMAP {
|
|||
}
|
||||
}
|
||||
|
||||
return Ok(Resource {
|
||||
content_type: "text/plain; version=0.0.4",
|
||||
contents: self.core.export_prometheus_metrics().await?.into_bytes(),
|
||||
}
|
||||
return Ok(Resource::new(
|
||||
"text/plain; version=0.0.4",
|
||||
self.core.export_prometheus_metrics().await?.into_bytes(),
|
||||
)
|
||||
.into_http_response());
|
||||
}
|
||||
}
|
||||
|
@ -406,6 +399,42 @@ impl JMAP {
|
|||
}
|
||||
_ => (),
|
||||
},
|
||||
#[cfg(feature = "enterprise")]
|
||||
"logo.svg" if self.core.is_enterprise_edition() => {
|
||||
// SPDX-SnippetBegin
|
||||
// SPDX-FileCopyrightText: 2020 Stalwart Labs Ltd <hello@stalw.art>
|
||||
// SPDX-License-Identifier: LicenseRef-SEL
|
||||
|
||||
match self
|
||||
.core
|
||||
.logo_resource(
|
||||
req.headers()
|
||||
.get(header::HOST)
|
||||
.and_then(|h| h.to_str().ok())
|
||||
.map(|h| h.rsplit_once(':').map_or(h, |(h, _)| h))
|
||||
.unwrap_or_default(),
|
||||
)
|
||||
.await
|
||||
{
|
||||
Ok(Some(resource)) => {
|
||||
return Ok(resource.into_http_response());
|
||||
}
|
||||
Ok(None) => (),
|
||||
Err(err) => {
|
||||
trc::error!(err.span_id(session.session_id));
|
||||
}
|
||||
}
|
||||
|
||||
let resource = self.inner.webadmin.get("logo.svg").await?;
|
||||
|
||||
return if !resource.is_empty() {
|
||||
Ok(resource.into_http_response())
|
||||
} else {
|
||||
Err(trc::ResourceEvent::NotFound.into_err())
|
||||
};
|
||||
|
||||
// SPDX-SnippetEnd
|
||||
}
|
||||
_ => {
|
||||
let path = req.uri().path();
|
||||
let resource = self
|
||||
|
@ -895,7 +924,7 @@ impl ToRequestError for trc::Error {
|
|||
},
|
||||
trc::EventType::Auth(cause) => match cause {
|
||||
trc::AuthEvent::MissingTotp => {
|
||||
RequestError::blank(403, "TOTP code required", cause.message())
|
||||
RequestError::blank(402, "TOTP code required", cause.message())
|
||||
}
|
||||
trc::AuthEvent::TooManyAttempts => RequestError::too_many_auth_attempts(),
|
||||
_ => RequestError::unauthorized(),
|
||||
|
|
|
@ -115,10 +115,10 @@ impl JMAP {
|
|||
}
|
||||
for signature_id in signature_ids {
|
||||
if let (Some(algo), Some(pk), Some(selector)) = (
|
||||
keys.value(&format!("{signature_id}.algorithm"))
|
||||
keys.value(format!("{signature_id}.algorithm"))
|
||||
.and_then(|algo| algo.parse::<Algorithm>().ok()),
|
||||
keys.value(&format!("{signature_id}.private-key")),
|
||||
keys.value(&format!("{signature_id}.selector")),
|
||||
keys.value(format!("{signature_id}.private-key")),
|
||||
keys.value(format!("{signature_id}.selector")),
|
||||
) {
|
||||
match obtain_dkim_public_key(algo, pk) {
|
||||
Ok(public) => {
|
||||
|
|
|
@ -108,6 +108,7 @@ impl JMAP {
|
|||
let filter = params.get("filter");
|
||||
let page: usize = params.parse("page").unwrap_or(0);
|
||||
let limit: usize = params.parse("limit").unwrap_or(0);
|
||||
let count = params.get("count").is_some();
|
||||
|
||||
// Parse types
|
||||
let mut types = Vec::new();
|
||||
|
@ -181,13 +182,17 @@ impl JMAP {
|
|||
return Err(manage::enterprise());
|
||||
}
|
||||
|
||||
let principals = self
|
||||
let mut principals = self
|
||||
.core
|
||||
.storage
|
||||
.data
|
||||
.list_principals(filter, tenant, &types, &fields, page, limit)
|
||||
.await?;
|
||||
|
||||
if count {
|
||||
principals.items.clear();
|
||||
}
|
||||
|
||||
Ok(JsonResponse::new(json!({
|
||||
"data": principals,
|
||||
}))
|
||||
|
@ -356,9 +361,6 @@ impl JMAP {
|
|||
} else {
|
||||
expire_token = true;
|
||||
}
|
||||
if change.field == PrincipalField::Roles {
|
||||
needs_assert = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -69,11 +69,7 @@ impl JMAP {
|
|||
.to_vec()
|
||||
};
|
||||
|
||||
Ok(Resource {
|
||||
content_type: "application/octet-stream",
|
||||
contents,
|
||||
}
|
||||
.into_http_response())
|
||||
Ok(Resource::new("application/octet-stream", contents).into_http_response())
|
||||
}
|
||||
(Some("purge"), Some("blob"), _, &Method::GET) => {
|
||||
// Validate the access token
|
||||
|
|
|
@ -200,10 +200,9 @@ impl JMAP {
|
|||
Property::Value,
|
||||
)
|
||||
.await?
|
||||
.map(|value| {
|
||||
.inspect(|value| {
|
||||
was_active = value.inner.properties.get(&Property::IsActive)
|
||||
== Some(&Value::Bool(true));
|
||||
value
|
||||
})
|
||||
.ok_or_else(|| {
|
||||
trc::StoreEvent::NotFound
|
||||
|
|
|
@ -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.4"
|
||||
version = "0.10.0"
|
||||
edition = "2021"
|
||||
resolver = "2"
|
||||
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
[package]
|
||||
name = "managesieve"
|
||||
version = "0.9.4"
|
||||
version = "0.10.0"
|
||||
edition = "2021"
|
||||
resolver = "2"
|
||||
|
||||
|
|
|
@ -1,11 +1,10 @@
|
|||
[package]
|
||||
name = "nlp"
|
||||
version = "0.9.4"
|
||||
version = "0.10.0"
|
||||
edition = "2021"
|
||||
resolver = "2"
|
||||
|
||||
[dependencies]
|
||||
utils = { path = "../utils" }
|
||||
xxhash-rust = { version = "0.8.5", features = ["xxh3"] }
|
||||
farmhash = "1.1.5"
|
||||
siphasher = "1.0"
|
||||
|
@ -20,6 +19,7 @@ jieba-rs = "0.7" # Chinese stemmer
|
|||
phf = { version = "0.11", features = ["macros"] }
|
||||
lru-cache = "0.1.2"
|
||||
parking_lot = "0.12.1"
|
||||
psl = "2"
|
||||
|
||||
[features]
|
||||
test_mode = []
|
||||
|
|
|
@ -6,8 +6,6 @@
|
|||
|
||||
use std::borrow::Cow;
|
||||
|
||||
use utils::suffixlist::PublicSuffix;
|
||||
|
||||
use crate::{
|
||||
language::{
|
||||
detect::{LanguageDetector, MIN_LANGUAGE_SCORE},
|
||||
|
@ -21,9 +19,9 @@ use crate::{
|
|||
},
|
||||
};
|
||||
|
||||
pub struct BayesTokenizer<'x, 'y> {
|
||||
pub struct BayesTokenizer<'x> {
|
||||
text: &'x str,
|
||||
tokenizer: TypesTokenizer<'x, 'y>,
|
||||
tokenizer: TypesTokenizer<'x>,
|
||||
stemmer: Stemmer,
|
||||
stop_words: Option<&'static phf::Set<&'static str>>,
|
||||
tokens: Vec<Cow<'x, str>>,
|
||||
|
@ -36,8 +34,8 @@ enum Stemmer {
|
|||
None,
|
||||
}
|
||||
|
||||
impl<'x, 'y> BayesTokenizer<'x, 'y> {
|
||||
pub fn new(text: &'x str, suffixes: &'y PublicSuffix) -> Self {
|
||||
impl<'x> BayesTokenizer<'x> {
|
||||
pub fn new(text: &'x str) -> Self {
|
||||
// Detect language
|
||||
let (mut language, score) =
|
||||
LanguageDetector::detect_single(text).unwrap_or((Language::English, 1.0));
|
||||
|
@ -47,7 +45,7 @@ impl<'x, 'y> BayesTokenizer<'x, 'y> {
|
|||
|
||||
Self {
|
||||
text,
|
||||
tokenizer: TypesTokenizer::new(text, suffixes),
|
||||
tokenizer: TypesTokenizer::new(text),
|
||||
stemmer: match language {
|
||||
Language::Mandarin => Stemmer::Mandarin,
|
||||
Language::Japanese => Stemmer::Japanese,
|
||||
|
@ -61,7 +59,7 @@ impl<'x, 'y> BayesTokenizer<'x, 'y> {
|
|||
}
|
||||
}
|
||||
|
||||
impl<'x, 'y> Iterator for BayesTokenizer<'x, 'y> {
|
||||
impl<'x> Iterator for BayesTokenizer<'x> {
|
||||
type Item = Cow<'x, str>;
|
||||
|
||||
fn next(&mut self) -> Option<Self::Item> {
|
||||
|
@ -1149,8 +1147,6 @@ pub static SYMBOLS: phf::Set<char> = phf::phf_set! {
|
|||
mod tests {
|
||||
use std::borrow::Cow;
|
||||
|
||||
use utils::suffixlist::PublicSuffix;
|
||||
|
||||
use crate::bayes::tokenize::BayesTokenizer;
|
||||
|
||||
#[test]
|
||||
|
@ -1236,10 +1232,8 @@ mod tests {
|
|||
("시작이 반이다", vec!["시작이", "반이다"]),
|
||||
];
|
||||
|
||||
let suffixes = PublicSuffix::default();
|
||||
|
||||
for (input, expect) in inputs.iter() {
|
||||
let input = BayesTokenizer::new(input, &suffixes).collect::<Vec<_>>();
|
||||
let input = BayesTokenizer::new(input).collect::<Vec<_>>();
|
||||
let expect = expect.iter().copied().map(Cow::from).collect::<Vec<_>>();
|
||||
|
||||
assert_eq!(input, expect,);
|
||||
|
|
|
@ -6,8 +6,6 @@ pub mod tokenizers;
|
|||
mod test {
|
||||
use std::fs;
|
||||
|
||||
use utils::suffixlist::PublicSuffix;
|
||||
|
||||
use crate::{
|
||||
bayes::{tokenize::BayesTokenizer, BayesClassifier, BayesModel},
|
||||
tokenizers::osb::{OsbToken, OsbTokenizer},
|
||||
|
@ -19,16 +17,12 @@ mod test {
|
|||
let db =
|
||||
fs::read_to_string("/Users/me/code/mail-server/_ignore/spam_or_not_spam.csv").unwrap();
|
||||
let mut bayes = BayesModel::default();
|
||||
let suffixes = PublicSuffix::default();
|
||||
|
||||
for line in db.lines() {
|
||||
let (text, is_spam) = line.rsplit_once(',').unwrap();
|
||||
let is_spam = is_spam == "1";
|
||||
|
||||
bayes.train(
|
||||
OsbTokenizer::new(BayesTokenizer::new(text, &suffixes), 5),
|
||||
is_spam,
|
||||
);
|
||||
bayes.train(OsbTokenizer::new(BayesTokenizer::new(text), 5), is_spam);
|
||||
}
|
||||
println!("Ham: {} Spam: {}", bayes.ham_learns, bayes.spam_learns,);
|
||||
fs::write(
|
||||
|
@ -46,7 +40,6 @@ mod test {
|
|||
)
|
||||
.unwrap();
|
||||
let bayes = BayesClassifier::new();
|
||||
let suffixes = PublicSuffix::default();
|
||||
|
||||
for text in [
|
||||
"i am attaching to this email a presentation to integrate the spreadsheet into our server",
|
||||
|
@ -58,7 +51,7 @@ mod test {
|
|||
"{:?} -> {}",
|
||||
text,
|
||||
bayes
|
||||
.classify(OsbTokenizer::new(BayesTokenizer::new(text, &suffixes), 5).filter_map(|x| model.weights.get(&x.inner).map(|w| {
|
||||
.classify(OsbTokenizer::new(BayesTokenizer::new(text), 5).filter_map(|x| model.weights.get(&x.inner).map(|w| {
|
||||
OsbToken {
|
||||
idx: x.idx,
|
||||
inner: *w,
|
||||
|
|
|
@ -37,7 +37,7 @@ impl<'x> InnerToken<'x> for Cow<'x, str> {
|
|||
}
|
||||
|
||||
fn is_alphabetic_8bit(&self) -> bool {
|
||||
!self.chars().all(|c| c.is_ascii())
|
||||
!self.is_ascii()
|
||||
}
|
||||
|
||||
fn unwrap_alphabetic(self) -> Cow<'x, str> {
|
||||
|
|
|
@ -6,13 +6,10 @@
|
|||
|
||||
use std::str::CharIndices;
|
||||
|
||||
use utils::suffixlist::PublicSuffix;
|
||||
|
||||
use super::Token;
|
||||
|
||||
pub struct TypesTokenizer<'x, 'y> {
|
||||
pub struct TypesTokenizer<'x> {
|
||||
text: &'x str,
|
||||
suffixes: &'y PublicSuffix,
|
||||
iter: CharIndices<'x>,
|
||||
tokens: Vec<Token<TokenType<&'x str>>>,
|
||||
peek_pos: usize,
|
||||
|
@ -45,7 +42,7 @@ pub enum TokenType<T> {
|
|||
|
||||
impl Copy for Token<TokenType<&'_ str>> {}
|
||||
|
||||
impl<'x, 'y> Iterator for TypesTokenizer<'x, 'y> {
|
||||
impl<'x> Iterator for TypesTokenizer<'x> {
|
||||
type Item = Token<TokenType<&'x str>>;
|
||||
|
||||
fn next(&mut self) -> Option<Self::Item> {
|
||||
|
@ -58,7 +55,7 @@ impl<'x, 'y> Iterator for TypesTokenizer<'x, 'y> {
|
|||
&& matches!(
|
||||
token.word,
|
||||
TokenType::Alphabetic(t) | TokenType::Alphanumeric(t)
|
||||
if t.len() <= 8 && t.chars().all(|c| c.is_ascii()))
|
||||
if t.len() <= 8 && t.is_ascii())
|
||||
&& self.try_skip_url_scheme()
|
||||
{
|
||||
if let Some(url) = self.try_parse_url(token.into()) {
|
||||
|
@ -111,15 +108,14 @@ impl<'x, 'y> Iterator for TypesTokenizer<'x, 'y> {
|
|||
}
|
||||
}
|
||||
|
||||
impl<'x, 'y> TypesTokenizer<'x, 'y> {
|
||||
pub fn new(text: &'x str, suffixes: &'y PublicSuffix) -> Self {
|
||||
impl<'x> TypesTokenizer<'x> {
|
||||
pub fn new(text: &'x str) -> Self {
|
||||
Self {
|
||||
text,
|
||||
iter: text.char_indices(),
|
||||
tokens: Vec::new(),
|
||||
eof: false,
|
||||
peek_pos: 0,
|
||||
suffixes,
|
||||
last_ch_is_space: false,
|
||||
last_token_is_dot: false,
|
||||
tokenize_urls: true,
|
||||
|
@ -327,8 +323,13 @@ impl<'x, 'y> TypesTokenizer<'x, 'y> {
|
|||
while let Some(token) = self.peek() {
|
||||
match token.word {
|
||||
TokenType::Alphabetic(text) | TokenType::Alphanumeric(text) => {
|
||||
last_label_is_tld =
|
||||
text.len() >= 2 && self.suffixes.contains(&text.to_ascii_lowercase());
|
||||
last_label_is_tld = text.len() >= 2
|
||||
&& psl::Psl::find(
|
||||
&psl::List,
|
||||
[text.to_ascii_lowercase().as_bytes()].into_iter(),
|
||||
)
|
||||
.typ
|
||||
.is_some();
|
||||
text_count += 1;
|
||||
}
|
||||
TokenType::Integer(text) => {
|
||||
|
@ -552,8 +553,13 @@ impl<'x, 'y> TypesTokenizer<'x, 'y> {
|
|||
.map(|(from, to)| (from, to, true));
|
||||
}
|
||||
TokenType::Alphabetic(text) | TokenType::Alphanumeric(text) if text.len() <= 63 => {
|
||||
last_label_is_tld =
|
||||
text.len() >= 2 && self.suffixes.contains(&text.to_ascii_lowercase());
|
||||
last_label_is_tld = text.len() >= 2
|
||||
&& psl::Psl::find(
|
||||
&psl::List,
|
||||
[text.to_ascii_lowercase().as_bytes()].into_iter(),
|
||||
)
|
||||
.typ
|
||||
.is_some();
|
||||
has_alpha = true;
|
||||
last_ch = 0;
|
||||
}
|
||||
|
@ -691,7 +697,7 @@ impl<'x, 'y> TypesTokenizer<'x, 'y> {
|
|||
(TokenType::Punctuation('/'), State::Slash2) => return true,
|
||||
(TokenType::Punctuation('+'), State::None) => State::PlusAlpha,
|
||||
(TokenType::Alphabetic(t) | TokenType::Alphanumeric(t), State::PlusAlpha)
|
||||
if t.chars().all(|c| c.is_ascii()) =>
|
||||
if t.is_ascii() =>
|
||||
{
|
||||
State::Colon
|
||||
}
|
||||
|
@ -748,17 +754,10 @@ impl<T> TokenType<T> {
|
|||
#[cfg(test)]
|
||||
mod test {
|
||||
|
||||
use utils::suffixlist::PublicSuffix;
|
||||
|
||||
use super::{TokenType, TypesTokenizer};
|
||||
|
||||
#[test]
|
||||
fn type_tokenizer() {
|
||||
let mut suffixes = PublicSuffix::default();
|
||||
suffixes.suffixes.insert("com".to_string());
|
||||
suffixes.suffixes.insert("co".to_string());
|
||||
suffixes.suffixes.insert("org".to_string());
|
||||
|
||||
// Credits: test suite from linkify crate
|
||||
for (text, expected) in [
|
||||
("", vec![]),
|
||||
|
@ -2862,7 +2861,7 @@ mod test {
|
|||
],
|
||||
),
|
||||
] {
|
||||
let result = TypesTokenizer::new(text, &suffixes)
|
||||
let result = TypesTokenizer::new(text)
|
||||
.map(|t| t.word)
|
||||
.collect::<Vec<_>>();
|
||||
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
[package]
|
||||
name = "pop3"
|
||||
version = "0.9.4"
|
||||
version = "0.10.0"
|
||||
edition = "2021"
|
||||
resolver = "2"
|
||||
|
||||
|
|
|
@ -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.4"
|
||||
version = "0.10.0"
|
||||
edition = "2021"
|
||||
resolver = "2"
|
||||
|
||||
|
@ -18,7 +18,7 @@ nlp = { path = "../nlp" }
|
|||
directory = { path = "../directory" }
|
||||
common = { path = "../common" }
|
||||
trc = { path = "../trc" }
|
||||
mail-auth = { version = "0.4" }
|
||||
mail-auth = { version = "0.5" }
|
||||
mail-send = { version = "0.4", default-features = false, features = ["cram-md5", "ring", "tls12"] }
|
||||
mail-parser = { version = "0.9", features = ["full_encoding", "ludicrous_mode"] }
|
||||
mail-builder = { version = "0.3", features = ["ludicrous_mode"] }
|
||||
|
|
|
@ -14,6 +14,7 @@ use std::{
|
|||
use common::{
|
||||
config::smtp::{auth::VerifyStrategy, session::Stage},
|
||||
listener::SessionStream,
|
||||
psl,
|
||||
scripts::ScriptModification,
|
||||
};
|
||||
use mail_auth::{
|
||||
|
@ -249,6 +250,7 @@ impl<T: SessionStream> Session<T> {
|
|||
&self.data.helo_domain
|
||||
},
|
||||
spf_output,
|
||||
|domain| psl::domain_str(domain).unwrap_or(domain),
|
||||
)
|
||||
.await;
|
||||
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
[package]
|
||||
name = "store"
|
||||
version = "0.9.4"
|
||||
version = "0.10.0"
|
||||
edition = "2021"
|
||||
resolver = "2"
|
||||
|
||||
|
|
|
@ -110,7 +110,7 @@ impl MysqlStore {
|
|||
SUBSPACE_TELEMETRY_INDEX,
|
||||
] {
|
||||
let table = char::from(table);
|
||||
conn.query_drop(&format!(
|
||||
conn.query_drop(format!(
|
||||
"CREATE TABLE IF NOT EXISTS {table} (
|
||||
k TINYBLOB,
|
||||
v MEDIUMBLOB NOT NULL,
|
||||
|
@ -121,7 +121,7 @@ impl MysqlStore {
|
|||
.map_err(into_error)?;
|
||||
}
|
||||
|
||||
conn.query_drop(&format!(
|
||||
conn.query_drop(format!(
|
||||
"CREATE TABLE IF NOT EXISTS {} (
|
||||
k TINYBLOB,
|
||||
v LONGBLOB NOT NULL,
|
||||
|
@ -139,7 +139,7 @@ impl MysqlStore {
|
|||
SUBSPACE_BITMAP_TEXT,
|
||||
] {
|
||||
let table = char::from(table);
|
||||
conn.query_drop(&format!(
|
||||
conn.query_drop(format!(
|
||||
"CREATE TABLE IF NOT EXISTS {table} (
|
||||
k BLOB,
|
||||
PRIMARY KEY (k(400))
|
||||
|
@ -150,7 +150,7 @@ impl MysqlStore {
|
|||
}
|
||||
|
||||
for table in [SUBSPACE_COUNTER, SUBSPACE_QUOTA] {
|
||||
conn.query_drop(&format!(
|
||||
conn.query_drop(format!(
|
||||
"CREATE TABLE IF NOT EXISTS {} (
|
||||
k TINYBLOB,
|
||||
v BIGINT NOT NULL DEFAULT 0,
|
||||
|
|
|
@ -22,7 +22,7 @@ impl MysqlStore {
|
|||
{
|
||||
let mut conn = self.conn_pool.get_conn().await.map_err(into_error)?;
|
||||
let s = conn
|
||||
.prep(&format!(
|
||||
.prep(format!(
|
||||
"SELECT v FROM {} WHERE k = ?",
|
||||
char::from(key.subspace())
|
||||
))
|
||||
|
@ -54,7 +54,7 @@ impl MysqlStore {
|
|||
|
||||
let mut bm = RoaringBitmap::new();
|
||||
let s = conn
|
||||
.prep(&format!("SELECT k FROM {table} WHERE k >= ? AND k <= ?"))
|
||||
.prep(format!("SELECT k FROM {table} WHERE k >= ? AND k <= ?"))
|
||||
.await
|
||||
.map_err(into_error)?;
|
||||
let mut rows = conn
|
||||
|
@ -147,7 +147,7 @@ impl MysqlStore {
|
|||
let key = key.serialize(0);
|
||||
let mut conn = self.conn_pool.get_conn().await.map_err(into_error)?;
|
||||
let s = conn
|
||||
.prep(&format!("SELECT v FROM {table} WHERE k = ?"))
|
||||
.prep(format!("SELECT v FROM {table} WHERE k = ?"))
|
||||
.await
|
||||
.map_err(into_error)?;
|
||||
match conn.exec_first::<i64, _, _>(&s, (key,)).await {
|
||||
|
|
|
@ -108,10 +108,10 @@ impl MysqlStore {
|
|||
let exists = asserted_values.get(&key);
|
||||
let s = if let Some(exists) = exists {
|
||||
if *exists {
|
||||
trx.prep(&format!("UPDATE {} SET v = :v WHERE k = :k", table))
|
||||
trx.prep(format!("UPDATE {} SET v = :v WHERE k = :k", table))
|
||||
.await?
|
||||
} else {
|
||||
trx.prep(&format!(
|
||||
trx.prep(format!(
|
||||
"INSERT INTO {} (k, v) VALUES (:k, :v)",
|
||||
table
|
||||
))
|
||||
|
@ -120,7 +120,7 @@ impl MysqlStore {
|
|||
} else {
|
||||
trx
|
||||
.prep(
|
||||
&format!("INSERT INTO {} (k, v) VALUES (:k, :v) ON DUPLICATE KEY UPDATE v = VALUES(v)", table),
|
||||
format!("INSERT INTO {} (k, v) VALUES (:k, :v) ON DUPLICATE KEY UPDATE v = VALUES(v)", table),
|
||||
)
|
||||
.await?
|
||||
};
|
||||
|
@ -149,7 +149,7 @@ impl MysqlStore {
|
|||
ValueOp::AtomicAdd(by) => {
|
||||
if *by >= 0 {
|
||||
let s = trx
|
||||
.prep(&format!(
|
||||
.prep(format!(
|
||||
concat!(
|
||||
"INSERT INTO {} (k, v) VALUES (?, ?) ",
|
||||
"ON DUPLICATE KEY UPDATE v = v + VALUES(v)"
|
||||
|
@ -160,14 +160,14 @@ impl MysqlStore {
|
|||
trx.exec_drop(&s, (key, by)).await?;
|
||||
} else {
|
||||
let s = trx
|
||||
.prep(&format!("UPDATE {table} SET v = v + ? WHERE k = ?"))
|
||||
.prep(format!("UPDATE {table} SET v = v + ? WHERE k = ?"))
|
||||
.await?;
|
||||
trx.exec_drop(&s, (by, key)).await?;
|
||||
}
|
||||
}
|
||||
ValueOp::AddAndGet(by) => {
|
||||
let s = trx
|
||||
.prep(&format!(
|
||||
.prep(format!(
|
||||
concat!(
|
||||
"INSERT INTO {} (k, v) VALUES (:k, LAST_INSERT_ID(:v)) ",
|
||||
"ON DUPLICATE KEY UPDATE v = LAST_INSERT_ID(v + :v)"
|
||||
|
@ -190,7 +190,7 @@ impl MysqlStore {
|
|||
}
|
||||
ValueOp::Clear => {
|
||||
let s = trx
|
||||
.prep(&format!("DELETE FROM {} WHERE k = ?", table))
|
||||
.prep(format!("DELETE FROM {} WHERE k = ?", table))
|
||||
.await?;
|
||||
trx.exec_drop(&s, (key,)).await?;
|
||||
}
|
||||
|
@ -256,11 +256,11 @@ impl MysqlStore {
|
|||
if is_document_id {
|
||||
trx.prep("INSERT INTO b (k) VALUES (?)").await?
|
||||
} else {
|
||||
trx.prep(&format!("INSERT IGNORE INTO {} (k) VALUES (?)", table))
|
||||
trx.prep(format!("INSERT IGNORE INTO {} (k) VALUES (?)", table))
|
||||
.await?
|
||||
}
|
||||
} else {
|
||||
trx.prep(&format!("DELETE FROM {} WHERE k = ?", table))
|
||||
trx.prep(format!("DELETE FROM {} WHERE k = ?", table))
|
||||
.await?
|
||||
};
|
||||
|
||||
|
@ -301,7 +301,7 @@ impl MysqlStore {
|
|||
let table = char::from(class.subspace(collection));
|
||||
|
||||
let s = trx
|
||||
.prep(&format!("SELECT v FROM {} WHERE k = ? FOR UPDATE", table))
|
||||
.prep(format!("SELECT v FROM {} WHERE k = ? FOR UPDATE", table))
|
||||
.await?;
|
||||
let (exists, matches) = trx
|
||||
.exec_first::<Vec<u8>, _, _>(&s, (&key,))
|
||||
|
@ -324,7 +324,7 @@ impl MysqlStore {
|
|||
let mut conn = self.conn_pool.get_conn().await.map_err(into_error)?;
|
||||
for subspace in [SUBSPACE_QUOTA, SUBSPACE_COUNTER] {
|
||||
let s = conn
|
||||
.prep(&format!("DELETE FROM {} WHERE v = 0", char::from(subspace),))
|
||||
.prep(format!("DELETE FROM {} WHERE v = 0", char::from(subspace),))
|
||||
.await
|
||||
.map_err(into_error)?;
|
||||
conn.exec_drop(&s, ()).await.map_err(into_error)?;
|
||||
|
@ -337,7 +337,7 @@ impl MysqlStore {
|
|||
let mut conn = self.conn_pool.get_conn().await.map_err(into_error)?;
|
||||
|
||||
let s = conn
|
||||
.prep(&format!(
|
||||
.prep(format!(
|
||||
"DELETE FROM {} WHERE k >= ? AND k < ?",
|
||||
char::from(from.subspace()),
|
||||
))
|
||||
|
|
|
@ -1,12 +1,12 @@
|
|||
[package]
|
||||
name = "trc"
|
||||
version = "0.9.4"
|
||||
version = "0.10.0"
|
||||
edition = "2021"
|
||||
resolver = "2"
|
||||
|
||||
[dependencies]
|
||||
event_macro = { path = "./event-macro" }
|
||||
mail-auth = { version = "0.4" }
|
||||
mail-auth = { version = "0.5" }
|
||||
mail-parser = { version = "0.9", features = ["full_encoding", "ludicrous_mode"] }
|
||||
base64 = "0.22.1"
|
||||
serde = "1.0"
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
[package]
|
||||
name = "utils"
|
||||
version = "0.9.4"
|
||||
version = "0.10.0"
|
||||
edition = "2021"
|
||||
resolver = "2"
|
||||
|
||||
|
@ -12,7 +12,7 @@ rustls-pki-types = { version = "1" }
|
|||
tokio = { version = "1.23", features = ["net", "macros"] }
|
||||
tokio-rustls = { version = "0.26", default-features = false, features = ["ring", "tls12"] }
|
||||
serde = { version = "1.0", features = ["derive"]}
|
||||
mail-auth = { version = "0.4" }
|
||||
mail-auth = { version = "0.5" }
|
||||
smtp-proto = { version = "0.1" }
|
||||
mail-send = { version = "0.4", default-features = false, features = ["cram-md5", "ring", "tls12"] }
|
||||
dashmap = "6.0"
|
||||
|
|
|
@ -12,7 +12,6 @@ pub mod glob;
|
|||
pub mod lru_cache;
|
||||
pub mod map;
|
||||
pub mod snowflake;
|
||||
pub mod suffixlist;
|
||||
pub mod url_params;
|
||||
|
||||
use rustls::{
|
||||
|
|
|
@ -31,7 +31,7 @@ trc = { path = "../crates/trc" }
|
|||
managesieve = { path = "../crates/managesieve", features = ["test_mode"] }
|
||||
smtp-proto = { version = "0.1" }
|
||||
mail-send = { version = "0.4", default-features = false, features = ["cram-md5", "ring", "tls12"] }
|
||||
mail-auth = { version = "0.4", features = ["test"] }
|
||||
mail-auth = { version = "0.5", features = ["test"] }
|
||||
sieve-rs = { version = "0.5" }
|
||||
utils = { path = "../crates/utils", features = ["test_mode"] }
|
||||
jmap-client = { version = "0.3", features = ["websockets", "debug", "async"] }
|
||||
|
|
|
@ -104,6 +104,7 @@ pub async fn test(params: &mut JMAPTest) {
|
|||
}
|
||||
.into(),
|
||||
metrics_alerts: parse_metric_alerts(&mut config),
|
||||
logo_url: None,
|
||||
}
|
||||
.into();
|
||||
config.assert_no_errors();
|
||||
|
@ -160,6 +161,7 @@ impl EnterpriseCore for Core {
|
|||
trace_store: None,
|
||||
metrics_store: None,
|
||||
metrics_alerts: vec![],
|
||||
logo_url: None,
|
||||
}
|
||||
.into();
|
||||
self
|
||||
|
|
|
@ -13,8 +13,6 @@ use directory::{
|
|||
backend::internal::{PrincipalField, PrincipalUpdate, PrincipalValue},
|
||||
Permission, Principal, Type,
|
||||
};
|
||||
use hyper::header::TE;
|
||||
use rayon::vec;
|
||||
use utils::BlobHash;
|
||||
|
||||
use crate::jmap::assert_is_empty;
|
||||
|
@ -360,12 +358,12 @@ pub async fn test(params: &JMAPTest) {
|
|||
)
|
||||
.await
|
||||
.unwrap()
|
||||
.expect_error("notFound");
|
||||
.expect_request_error("Forbidden");
|
||||
tenant_api
|
||||
.get::<()>("/api/principal/foobar")
|
||||
.await
|
||||
.unwrap()
|
||||
.expect_error("notFound");
|
||||
.expect_request_error("Forbidden");
|
||||
tenant_api
|
||||
.get::<()>("/api/principal?type=tenant")
|
||||
.await
|
||||
|
|
|
@ -131,10 +131,7 @@ pub fn spawn_mock_webhook_endpoint() -> Arc<MockWebhookEndpoint> {
|
|||
endpoint.events.lock().extend(request.events);
|
||||
|
||||
Ok::<_, hyper::Error>(
|
||||
Resource {
|
||||
content_type: "application/json",
|
||||
contents: "[]".to_string().into_bytes(),
|
||||
}
|
||||
Resource::new("application/json", "[]".to_string().into_bytes())
|
||||
.into_http_response().build(),
|
||||
)
|
||||
} else {
|
||||
|
|
|
@ -78,8 +78,8 @@ path = "{PATH}/test_antispam.db"
|
|||
#url = "redis://127.0.0.1"
|
||||
|
||||
[lookup]
|
||||
"spam-free" = {"gmail.com", "googlemail.com", "yahoomail.com", "*.freemail.org"}
|
||||
"spam-disposable" = {"guerrillamail.com", "*.disposable.org"}
|
||||
"spam-free" = {"gmail.com", "googlemail.com", "yahoomail.com", "*freemail.org"}
|
||||
"spam-disposable" = {"guerrillamail.com", "*disposable.org"}
|
||||
"spam-redirect" = {"bit.ly", "redirect.io", "redirect.me", "redirect.org", "redirect.com", "redirect.net", "t.ly", "tinyurl.com"}
|
||||
"spam-dmarc" = {"dmarc-allow.org"}
|
||||
"spam-spdk" = {"spf-dkim-allow.org"}
|
||||
|
|
|
@ -809,10 +809,7 @@ pub fn spawn_mock_mta_hook_server() -> watch::Sender<bool> {
|
|||
let response = handle_mta_hook(request, tests);
|
||||
|
||||
Ok::<_, hyper::Error>(
|
||||
Resource {
|
||||
content_type: "application/json",
|
||||
contents: serde_json::to_string(&response).unwrap().into_bytes(),
|
||||
}
|
||||
Resource::new("application/json", serde_json::to_string(&response).unwrap().into_bytes())
|
||||
.into_http_response().build(),
|
||||
)
|
||||
}
|
||||
|
|
|
@ -35,7 +35,6 @@ use mail_auth::{
|
|||
Resolver, MX,
|
||||
};
|
||||
use rustls_pki_types::CertificateDer;
|
||||
use utils::suffixlist::PublicSuffix;
|
||||
|
||||
use crate::smtp::{
|
||||
inbound::{TestMessage, TestQueueEvent, TestReportingEvent},
|
||||
|
@ -260,7 +259,6 @@ async fn dane_test() {
|
|||
tlsa: LruCache::with_capacity(10),
|
||||
mta_sts: LruCache::with_capacity(10),
|
||||
},
|
||||
psl: PublicSuffix::default(),
|
||||
};
|
||||
let r = SMTP {
|
||||
core: core.into(),
|
||||
|
|
Loading…
Reference in a new issue