diff --git a/Cargo.lock b/Cargo.lock index 876fbbcb..8f53946a 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -262,7 +262,7 @@ checksum = "5fd55a5ba1179988837d24ab4c7cc8ed6efdeff578ede0416b4225a5fca35bd0" dependencies = [ "proc-macro2", "quote", - "syn 2.0.37", + "syn 2.0.38", ] [[package]] @@ -284,7 +284,7 @@ checksum = "16e62a023e7c117e27523144c5d2459f4397fcc3cab0085af8e2224f643a0193" dependencies = [ "proc-macro2", "quote", - "syn 2.0.37", + "syn 2.0.38", ] [[package]] @@ -295,7 +295,7 @@ checksum = "bc00ceb34980c03614e35a3a4e218276a0a824e911d07651cd0d858a51e8c0f0" dependencies = [ "proc-macro2", "quote", - "syn 2.0.37", + "syn 2.0.38", ] [[package]] @@ -506,7 +506,7 @@ dependencies = [ "regex", "rustc-hash", "shlex", - "syn 2.0.37", + "syn 2.0.38", "which", ] @@ -665,9 +665,9 @@ checksum = "374d28ec25809ee0e23827c2ab573d729e293f281dfe393500e7ad618baa61c6" [[package]] name = "byteorder" -version = "1.4.3" +version = "1.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "14c189c53d098945499cdfa7ecc63567cf3886b3332b312a5b4585d8d3a6a610" +checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b" [[package]] name = "bytes" @@ -742,6 +742,17 @@ dependencies = [ "nom", ] +[[package]] +name = "cfb" +version = "0.7.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d38f2da7a0a2c4ccf0065be06397cc26a81f4e528be095826eee9d4adbb8c60f" +dependencies = [ + "byteorder", + "fnv", + "uuid", +] + [[package]] name = "cfb-mode" version = "0.8.2" @@ -833,7 +844,7 @@ dependencies = [ "heck", "proc-macro2", "quote", - "syn 2.0.37", + "syn 2.0.38", ] [[package]] @@ -1039,9 +1050,9 @@ dependencies = [ [[package]] name = "csv" -version = "1.2.2" +version = "1.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "626ae34994d3d8d668f4269922248239db4ae42d538b14c398b74a52208e8086" +checksum = "ac574ff4d437a7b5ad237ef331c17ccca63c46479e5b5453eb8e10bb99a759fe" dependencies = [ "csv-core", "itoa", @@ -1051,9 +1062,9 @@ dependencies = [ [[package]] name = "csv-core" -version = "0.1.10" +version = "0.1.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2b2466559f260f48ad25fe6317b3c8dac77b5bdb5763ac7d9d6103530663bc90" +checksum = "5efa2b3d7902f4b634a20cae3c9c4e6209dc4779feb6863329607560143efa70" dependencies = [ "memchr", ] @@ -1293,7 +1304,7 @@ checksum = "487585f4d0c6655fe74905e2504d8ad6908e4db67f744eb140876906c2f3175d" dependencies = [ "proc-macro2", "quote", - "syn 2.0.37", + "syn 2.0.38", ] [[package]] @@ -1407,9 +1418,9 @@ dependencies = [ [[package]] name = "elliptic-curve" -version = "0.13.5" +version = "0.13.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "968405c8fdc9b3bf4df0a6638858cc0b52462836ab6b1c87377785dd09cf1c0b" +checksum = "d97ca172ae9dc9f9b779a6e3a65d308f2af74e5b8c921299075bdb4a0370e914" dependencies = [ "base16ct", "crypto-bigint", @@ -1465,7 +1476,7 @@ dependencies = [ "heck", "proc-macro2", "quote", - "syn 2.0.37", + "syn 2.0.38", ] [[package]] @@ -1692,7 +1703,7 @@ checksum = "83c8d52fe8b46ab822b4decdcc0d6d85aeedfc98f0d52ba2bd4aec4a97807516" dependencies = [ "proc-macro2", "quote", - "syn 2.0.37", + "syn 2.0.38", "try_map", ] @@ -1778,7 +1789,7 @@ checksum = "89ca545a94061b6365f2c7355b4b32bd20df3ff95f02da9329b34ccc3bd6ee72" dependencies = [ "proc-macro2", "quote", - "syn 2.0.37", + "syn 2.0.38", ] [[package]] @@ -2153,7 +2164,7 @@ dependencies = [ [[package]] name = "hyper-util" version = "0.0.0" -source = "git+https://github.com/hyperium/hyper-util#d97181a278d9c59f1d7f2713732e400440861216" +source = "git+https://github.com/hyperium/hyper-util#63e84bf0603df79bf86b733e7376f1317bf2eefe" dependencies = [ "bytes", "futures-channel", @@ -2295,6 +2306,15 @@ dependencies = [ "unicode-width", ] +[[package]] +name = "infer" +version = "0.15.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cb33622da908807a06f9513c19b3c1ad50fab3e4137d82a78107d502075aa199" +dependencies = [ + "cfb", +] + [[package]] name = "inout" version = "0.1.3" @@ -2594,7 +2614,7 @@ dependencies = [ "log", "nom", "percent-encoding", - "ring", + "ring 0.16.20", "rustls 0.21.7", "rustls-native-certs", "thiserror", @@ -2624,9 +2644,9 @@ dependencies = [ [[package]] name = "libm" -version = "0.2.7" +version = "0.2.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f7012b1bbb0719e1097c47611d3898568c546d597c2e74d66f6087edd5233ff4" +checksum = "4ec2a862134d2a7d32d7983ddcdd1c4923530833c9f2ea1a44fc5fa473989058" [[package]] name = "librocksdb-sys" @@ -2724,8 +2744,8 @@ dependencies = [ [[package]] name = "mail-auth" -version = "0.3.3" -source = "git+https://github.com/stalwartlabs/mail-auth#1605fe274fd78f16b5fdce6fad042f4b0e00846f" +version = "0.3.5" +source = "git+https://github.com/stalwartlabs/mail-auth#191189df4506ed81937783fb01ded8b8bb50f7cf" dependencies = [ "ahash 0.8.3", "flate2", @@ -2734,7 +2754,7 @@ dependencies = [ "mail-parser", "parking_lot", "quick-xml 0.30.0", - "ring", + "ring 0.17.0", "rustls-pemfile", "serde", "serde_json", @@ -2745,7 +2765,7 @@ dependencies = [ [[package]] name = "mail-builder" version = "0.3.1" -source = "git+https://github.com/stalwartlabs/mail-builder#1eb0b5a72211c491cbe338920e8dfd3a675d6653" +source = "git+https://github.com/stalwartlabs/mail-builder#665f46db8542a97f5b86fd1ff9ea8d74d5ab7e4f" dependencies = [ "gethostname", ] @@ -2761,8 +2781,8 @@ dependencies = [ [[package]] name = "mail-send" -version = "0.4.0" -source = "git+https://github.com/stalwartlabs/mail-send#ffa60e3f653d0f4057b7c97d103751a80adc4c12" +version = "0.4.1" +source = "git+https://github.com/stalwartlabs/mail-send#53904ce6cf4fcb9a42a92a541f5d64d4d972d6cb" dependencies = [ "base64 0.20.0", "gethostname", @@ -3129,7 +3149,7 @@ checksum = "a948666b637a0f465e8564c73e89d4dde00d72d4d473cc972f390fc3dcee7d9c" dependencies = [ "proc-macro2", "quote", - "syn 2.0.37", + "syn 2.0.38", ] [[package]] @@ -3447,7 +3467,7 @@ dependencies = [ "phf_shared 0.11.2", "proc-macro2", "quote", - "syn 2.0.37", + "syn 2.0.38", ] [[package]] @@ -3485,7 +3505,7 @@ checksum = "4359fd9c9171ec6e8c62926d6faaf553a8dc3f64e1507e76da7911b4f6a04405" dependencies = [ "proc-macro2", "quote", - "syn 2.0.37", + "syn 2.0.38", ] [[package]] @@ -3564,7 +3584,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ae005bd773ab59b4725093fd7df83fd7892f7d8eafb48dbd7de6e024e4215f9d" dependencies = [ "proc-macro2", - "syn 2.0.37", + "syn 2.0.38", ] [[package]] @@ -3602,9 +3622,9 @@ dependencies = [ [[package]] name = "proc-macro2" -version = "1.0.67" +version = "1.0.68" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3d433d9f1a3e8c1263d9456598b16fec66f4acc9a74dacffd35c7bb09b3a1328" +checksum = "5b1106fec09662ec6dd98ccac0f81cef56984d0b49f75c92d8cbad76e20c005c" dependencies = [ "unicode-ident", ] @@ -3920,9 +3940,9 @@ checksum = "dbb5fb1acd8a1a18b3dd5be62d25485eb770e05afb408a9627d14d451bae12da" [[package]] name = "reqwest" -version = "0.11.20" +version = "0.11.22" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3e9ad3fe7488d7e34558a2033d45a0c90b72d97b4f80705666fea71472e2e6a1" +checksum = "046cd98826c46c2ac8ddecae268eb5c2e58628688a5fc7a2643704a73faba95b" dependencies = [ "base64 0.21.4", "bytes", @@ -3947,6 +3967,7 @@ dependencies = [ "serde", "serde_json", "serde_urlencoded", + "system-configuration", "tokio", "tokio-rustls", "tokio-util", @@ -3996,11 +4017,25 @@ dependencies = [ "libc", "once_cell", "spin 0.5.2", - "untrusted", + "untrusted 0.7.1", "web-sys", "winapi", ] +[[package]] +name = "ring" +version = "0.17.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fb9d44f9bf6b635117787f72416783eb7e4227aaf255e5ce739563d817176a7e" +dependencies = [ + "cc", + "getrandom 0.2.10", + "libc", + "spin 0.9.8", + "untrusted 0.9.0", + "windows-sys 0.48.0", +] + [[package]] name = "ripemd" version = "0.1.3" @@ -4163,9 +4198,9 @@ dependencies = [ [[package]] name = "rustix" -version = "0.38.15" +version = "0.38.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d2f9da0cbd88f9f09e7814e388301c8414c51c62aa6ce1e4b5c551d49d96e531" +checksum = "f25469e9ae0f3d0047ca8b93fc56843f38e6774f0914a107ff8b41be8be8e0b7" dependencies = [ "bitflags 2.4.0", "errno", @@ -4181,7 +4216,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1b80e3dec595989ea8510028f30c408a4630db12c9cbb8de34203b89d6577e99" dependencies = [ "log", - "ring", + "ring 0.16.20", "sct", "webpki", ] @@ -4193,7 +4228,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "cd8d6c9f025a446bc4d18ad9632e69aec8f287aa84499ee335599fabd20c3fd8" dependencies = [ "log", - "ring", + "ring 0.16.20", "rustls-webpki 0.101.6", "sct", ] @@ -4225,8 +4260,8 @@ version = "0.100.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5f6a5fc258f1c1276dfe3016516945546e2d5383911efc0fc4f1cdc5df3a4ae3" dependencies = [ - "ring", - "untrusted", + "ring 0.16.20", + "untrusted 0.7.1", ] [[package]] @@ -4235,8 +4270,8 @@ version = "0.101.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3c7d5dece342910d9ba34d259310cae3e0154b873b35408b787b59bce53d34fe" dependencies = [ - "ring", - "untrusted", + "ring 0.16.20", + "untrusted 0.7.1", ] [[package]] @@ -4302,8 +4337,8 @@ version = "0.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d53dcdb7c9f8158937a7981b48accfd39a43af418591a5d008c7b22b5e1b7ca4" dependencies = [ - "ring", - "untrusted", + "ring 0.16.20", + "untrusted 0.7.1", ] [[package]] @@ -4423,7 +4458,7 @@ checksum = "4eca7ac642d82aa35b60049a6eccb4be6be75e599bd2e9adb5f875a737654af2" dependencies = [ "proc-macro2", "quote", - "syn 2.0.37", + "syn 2.0.38", ] [[package]] @@ -4471,7 +4506,7 @@ checksum = "91d129178576168c589c9ec973feedf7d3126c01ac2bf08795109aa35b69fb8f" dependencies = [ "proc-macro2", "quote", - "syn 2.0.37", + "syn 2.0.38", ] [[package]] @@ -4545,9 +4580,9 @@ dependencies = [ [[package]] name = "sharded-slab" -version = "0.1.6" +version = "0.1.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c1b21f559e07218024e7e9f90f96f601825397de0e25420135f7f952453fed0b" +checksum = "f40ca3c46823713e0d4209592e8d6e826aa57e928f09752619fc696c499637f6" dependencies = [ "lazy_static", ] @@ -4567,7 +4602,7 @@ checksum = "a7cee0529a6d40f580e7a5e6c495c8fbfe21b7b52795ed4bb5e62cdf92bc6380" [[package]] name = "sieve-rs" version = "0.3.1" -source = "git+https://github.com/stalwartlabs/sieve#3039311edc66988223e0bc6029a23fb3f8931f94" +source = "git+https://github.com/stalwartlabs/sieve#c9288b62815610872e9f278b904e34d46124acb5" dependencies = [ "ahash 0.8.3", "bincode", @@ -4639,6 +4674,7 @@ dependencies = [ "hyper-util", "idna 0.4.0", "imagesize", + "infer", "lazy_static", "linkify", "lru-cache", @@ -4646,6 +4682,7 @@ dependencies = [ "mail-builder", "mail-parser", "mail-send", + "md5", "num_cpus", "parking_lot", "rand 0.8.5", @@ -5090,9 +5127,9 @@ dependencies = [ [[package]] name = "syn" -version = "2.0.37" +version = "2.0.38" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7303ef2c05cd654186cb250d29049a24840ca25d2747c25c0381c8d9e2f582e8" +checksum = "e96b79aaa137db8f61e26363a0c9b47d8b4ec75da28b7d1d614c2303e232408b" dependencies = [ "proc-macro2", "quote", @@ -5117,6 +5154,27 @@ dependencies = [ "unicode-xid", ] +[[package]] +name = "system-configuration" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ba3a3adc5c275d719af8cb4272ea1c4a6d668a777f37e115f6d11ddbc1c8e0e7" +dependencies = [ + "bitflags 1.3.2", + "core-foundation", + "system-configuration-sys", +] + +[[package]] +name = "system-configuration-sys" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a75fb188eb626b924683e3b95e3a48e63551fcfb51949de2f06a9d91dbee93c9" +dependencies = [ + "core-foundation-sys", + "libc", +] + [[package]] name = "tap" version = "1.0.1" @@ -5223,7 +5281,7 @@ checksum = "10712f02019e9288794769fba95cd6847df9874d49d871d062172f9dd41bc4cc" dependencies = [ "proc-macro2", "quote", - "syn 2.0.37", + "syn 2.0.38", ] [[package]] @@ -5335,7 +5393,7 @@ checksum = "630bdcf245f78637c13ec01ffae6187cca34625e8c63150d424b59e55af2675e" dependencies = [ "proc-macro2", "quote", - "syn 2.0.37", + "syn 2.0.38", ] [[package]] @@ -5492,7 +5550,7 @@ checksum = "5f4f31f56159e98206da9efd823404b79b6ef3143b4a7ab76e67b1751b25a4ab" dependencies = [ "proc-macro2", "quote", - "syn 2.0.37", + "syn 2.0.38", ] [[package]] @@ -5578,7 +5636,7 @@ dependencies = [ "ipnet", "once_cell", "rand 0.8.5", - "ring", + "ring 0.16.20", "rustls 0.21.7", "rustls-pemfile", "rustls-webpki 0.101.6", @@ -5684,9 +5742,18 @@ checksum = "42ff0bf0c66b8238c6f3b578df37d0b7848e55df8577b3f74f92a69acceeb825" [[package]] name = "typewit" -version = "1.7.0" +version = "1.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9aa6b48ea01ae418f25260e05d1a97c5616a4e2d796dc3ba1e5a236a5100ed35" +checksum = "6779a69cc5f9a7388274a0a8a353eb1c9e45195f9ae74a26690b055a7cf9592a" +dependencies = [ + "typewit_proc_macros", +] + +[[package]] +name = "typewit_proc_macros" +version = "1.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e36a83ea2b3c704935a01b4642946aadd445cea40b10935e3f8bd8052b8193d6" [[package]] name = "unicase" @@ -5774,6 +5841,12 @@ version = "0.7.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a156c684c91ea7d62626509bce3cb4e1d9ed5c4d978f7b4352658f96a4c26b4a" +[[package]] +name = "untrusted" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8ecb6da28b8a351d773b68d5825ac39017e680750f980f3a1a85cd8dd28a47c1" + [[package]] name = "url" version = "2.4.1" @@ -5897,7 +5970,7 @@ dependencies = [ "once_cell", "proc-macro2", "quote", - "syn 2.0.37", + "syn 2.0.38", "wasm-bindgen-shared", ] @@ -5931,7 +6004,7 @@ checksum = "54681b18a46765f095758388f2d0cf16eb8d4169b639ab575a8f5693af210c7b" dependencies = [ "proc-macro2", "quote", - "syn 2.0.37", + "syn 2.0.38", "wasm-bindgen-backend", "wasm-bindgen-shared", ] @@ -5971,8 +6044,8 @@ version = "0.22.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "07ecc0cd7cac091bf682ec5efa18b1cff79d617b84181f38b3951dbe135f607f" dependencies = [ - "ring", - "untrusted", + "ring 0.16.20", + "untrusted 0.7.1", ] [[package]] @@ -6291,7 +6364,7 @@ checksum = "ce36e65b0d2999d2aafac989fb249189a141aee1f53c612c1f37d72631959f69" dependencies = [ "proc-macro2", "quote", - "syn 2.0.37", + "syn 2.0.38", ] [[package]] diff --git a/crates/directory/src/config.rs b/crates/directory/src/config.rs index 991f4952..1afda89d 100644 --- a/crates/directory/src/config.rs +++ b/crates/directory/src/config.rs @@ -23,7 +23,7 @@ use bb8::{ManageConnection, Pool}; use regex::Regex; -use sieve::runtime::tests::glob::GlobPattern; +use sieve::runtime::{tests::glob::GlobPattern, Variable}; use std::{ fs::File, io::{BufRead, BufReader}, @@ -45,11 +45,11 @@ use crate::{ pub trait ConfigDirectory { fn parse_directory(&self) -> utils::config::Result; - fn parse_lookup_list( + fn parse_lookup_list( &self, - key: impl AsKey, + key: K, format: LookupFormat, - ) -> utils::config::Result; + ) -> utils::config::Result; } impl ConfigDirectory for Config { @@ -102,25 +102,42 @@ impl ConfigDirectory for Config { } } else { let key = ("directory", id, "lookup", lookup_id).as_key(); - let list = match self.property::((&key, "type"))? { - Some(lookup_type) => self.parse_lookup_list( - (&key, "values"), - LookupFormat { - lookup_type, - comment: self.value((&key, "comment")).map(|s| s.to_string()), - }, - )?, - None => self.parse_lookup_list( - key, - LookupFormat { - lookup_type: LookupType::List, - comment: None, - }, - )?, - }; - - Lookup::List { list } + match self.property::((&key, "type"))? { + Some(LookupType::Map) => Lookup::Map { + map: self.parse_lookup_list( + (&key, "values"), + LookupFormat { + lookup_type: LookupType::Map, + comment: self.value((&key, "comment")).map(|s| s.to_string()), + separator: self + .value((&key, "separator")) + .map(|s| s.to_string()), + }, + )?, + }, + Some(lookup_type) => Lookup::List { + list: self.parse_lookup_list( + (&key, "values"), + LookupFormat { + lookup_type, + comment: self.value((&key, "comment")).map(|s| s.to_string()), + separator: None, + }, + )?, + }, + None => Lookup::List { + list: self.parse_lookup_list( + key, + LookupFormat { + lookup_type: LookupType::List, + comment: None, + separator: None, + }, + )?, + }, + } }; + config .lookups .insert(format!("{id}/{lookup_id}"), Arc::new(lookup)); @@ -132,12 +149,12 @@ impl ConfigDirectory for Config { Ok(config) } - fn parse_lookup_list( + fn parse_lookup_list( &self, - key: impl AsKey, + key: K, format: LookupFormat, - ) -> utils::config::Result { - let mut list = LookupList::default(); + ) -> utils::config::Result { + let mut list = T::default(); let mut last_failed = false; for (_, mut value) in self.values(key.clone()) { if let Some(new_value) = value.strip_prefix("fallback+") { @@ -208,16 +225,46 @@ impl ConfigDirectory for Config { ) })?; } else { - list.insert(value.to_string(), format.lookup_type); + list.insert(value.to_string(), &format); } } Ok(list) } } -impl LookupList { - pub fn insert(&mut self, entry: String, format: LookupType) { - match format { +pub trait InsertLine: Default { + fn insert(&mut self, entry: String, format: &LookupFormat); + fn insert_lines( + &mut self, + reader: R, + format: &LookupFormat, + decompress: bool, + ) -> Result<(), std::io::Error> { + let reader: Box = if decompress { + Box::new(flate2::read::GzDecoder::new(reader)) + } else { + Box::new(reader) + }; + + for line in BufReader::new(reader).lines() { + let line_ = line?; + let line = line_.trim(); + if !line.is_empty() + && format + .comment + .as_ref() + .map_or(true, |c| !line.starts_with(c)) + { + self.insert(line.to_string(), format); + } + } + Ok(()) + } +} + +impl InsertLine for LookupList { + fn insert(&mut self, entry: String, format: &LookupFormat) { + match format.lookup_type { LookupType::List => { self.set.insert(entry); } @@ -255,34 +302,52 @@ impl LookupList { tracing::warn!("Invalid regular expression {:?}: {}", entry, err); } }, + LookupType::Map => unreachable!(), } } +} - pub fn insert_lines( - &mut self, - reader: R, - format: &LookupFormat, - decompress: bool, - ) -> Result<(), std::io::Error> { - let reader: Box = if decompress { - Box::new(flate2::read::GzDecoder::new(reader)) - } else { - Box::new(reader) - }; +impl InsertLine for AHashMap> { + fn insert(&mut self, entry: String, format: &LookupFormat) { + let (key, value) = entry + .split_once(format.separator.as_deref().unwrap_or(" ")) + .unwrap_or((entry.as_str(), "")); + let key = key.trim(); + if key.is_empty() { + return; + } else if value.is_empty() { + self.insert(key.to_string(), Variable::default()); + return; + } + let mut has_digit = false; + let mut has_dots = false; + let mut has_other = false; - for line in BufReader::new(reader).lines() { - let line_ = line?; - let line = line_.trim(); - if !line.is_empty() - && format - .comment - .as_ref() - .map_or(true, |c| !line.starts_with(c)) - { - self.insert(line.to_string(), format.lookup_type); + for (pos, ch) in value.bytes().enumerate() { + if ch.is_ascii_digit() { + has_digit = true; + } else if ch == b'.' { + has_dots = true; + } else if pos > 0 || ch != b'-' { + has_other = true; } } - Ok(()) + + let value = if has_other || !has_digit { + Variable::String(value.to_string()) + } else if has_dots { + value + .parse() + .map(Variable::Float) + .unwrap_or_else(|_| Variable::String(value.to_string())) + } else { + value + .parse() + .map(Variable::Integer) + .unwrap_or_else(|_| Variable::String(value.to_string())) + }; + + self.insert(key.to_string(), value); } } @@ -361,12 +426,14 @@ pub enum LookupType { List, Glob, Regex, + Map, } #[derive(Debug, Clone)] pub struct LookupFormat { pub lookup_type: LookupType, pub comment: Option, + pub separator: Option, } impl Default for LookupFormat { @@ -374,6 +441,7 @@ impl Default for LookupFormat { Self { lookup_type: LookupType::Glob, comment: Default::default(), + separator: Default::default(), } } } @@ -384,6 +452,7 @@ impl ParseValue for LookupType { "list" => Ok(LookupType::List), "glob" => Ok(LookupType::Glob), "regex" => Ok(LookupType::Regex), + "map" => Ok(LookupType::Map), _ => Err(format!( "Invalid value for lookup type {key:?}: {value:?}", key = key.as_key(), diff --git a/crates/directory/src/lib.rs b/crates/directory/src/lib.rs index a9ecac41..b64b3b18 100644 --- a/crates/directory/src/lib.rs +++ b/crates/directory/src/lib.rs @@ -28,7 +28,8 @@ use bb8::RunError; use imap::ImapError; use ldap3::LdapError; use mail_send::Credentials; -use sieve::runtime::tests::glob::GlobPattern; +use sieve::runtime::{tests::glob::GlobPattern, Variable}; +use smtp_proto::IntoString; use utils::config::DynValue; pub mod cache; @@ -108,6 +109,9 @@ pub enum Lookup { List { list: LookupList, }, + Map { + map: AHashMap>, + }, } #[derive(Debug, Default, Clone, PartialEq, Eq)] @@ -174,6 +178,36 @@ impl Lookup { } } Lookup::List { list } => list.contains(item).into(), + Lookup::Map { map } => map.contains_key(item).into(), + } + } + + pub async fn lookup(&self, item: &str) -> Option> { + match self { + Lookup::Directory { directory, query } => match directory.query(query, &[item]).await { + Ok(mut result) => match result.len() { + 1 => result.pop().map(Variable::from).unwrap(), + 0 => Variable::default(), + _ => Variable::Array(result.into_iter().map(Variable::from).collect()), + } + .into(), + Err(_) => None, + }, + Lookup::List { list } => Some(list.contains(item).into()), + Lookup::Map { map } => map.get(item).cloned(), + } + } +} + +impl From for Variable<'static> { + fn from(value: QueryColumn) -> Self { + match value { + QueryColumn::Integer(v) => Variable::Integer(v), + QueryColumn::Bool(v) => Variable::Integer(i64::from(v)), + QueryColumn::Float(v) => Variable::Float(v), + QueryColumn::Text(v) => Variable::String(v), + QueryColumn::Blob(v) => Variable::String(v.into_string()), + QueryColumn::Null => Variable::StringRef(""), } } } @@ -221,6 +255,7 @@ impl Debug for Lookup { f.debug_struct("Directory").field("query", query).finish() } Self::List { list } => f.debug_struct("List").field("list", list).finish(), + Self::Map { map } => f.debug_struct("Map").field("map", &map.keys()).finish(), } } } diff --git a/crates/smtp/Cargo.toml b/crates/smtp/Cargo.toml index bff08522..532be3c1 100644 --- a/crates/smtp/Cargo.toml +++ b/crates/smtp/Cargo.toml @@ -32,6 +32,7 @@ http-body-util = "0.1.0-rc.3" form_urlencoded = "1.1.0" sha1 = "0.10" sha2 = "0.10.6" +md5 = "0.7.0" rayon = "1.5" tracing = "0.1" parking_lot = "0.12" @@ -53,6 +54,7 @@ linkify = "0.10" idna = "0.4" decancer = "1.6.1" unicode-security = "0.1.0" +infer = "0.15.0" [features] test_mode = [] diff --git a/crates/smtp/src/inbound/data.rs b/crates/smtp/src/inbound/data.rs index 078de1ac..a1c87c5b 100644 --- a/crates/smtp/src/inbound/data.rs +++ b/crates/smtp/src/inbound/data.rs @@ -30,10 +30,11 @@ use std::{ }; use mail_auth::{ - common::headers::HeaderWriter, dmarc, AuthenticatedMessage, AuthenticationResults, DkimResult, - DmarcResult, ReceivedSpf, + common::{headers::HeaderWriter, verify::VerifySignature}, + dmarc, AuthenticatedMessage, AuthenticationResults, DkimResult, DmarcResult, ReceivedSpf, }; use mail_builder::headers::{date::Date, message_id::generate_message_id_header}; +use sieve::runtime::Variable; use smtp_proto::{ MAIL_BY_RETURN, RCPT_NOTIFY_DELAY, RCPT_NOTIFY_FAILURE, RCPT_NOTIFY_NEVER, RCPT_NOTIFY_SUCCESS, }; @@ -432,6 +433,20 @@ impl Session { .map(|r| r.result().as_str()) .unwrap_or_default(), ) + .set_variable( + "dkim.domains", + dkim_output + .iter() + .filter_map(|r| { + if matches!(r.result(), DkimResult::Pass) { + r.signature() + .map(|s| Variable::String(s.domain().to_lowercase())) + } else { + None + } + }) + .collect::>(), + ) .set_variable( "dmarc.result", dmarc_result diff --git a/crates/smtp/src/scripts/event_loop.rs b/crates/smtp/src/scripts/event_loop.rs index 17a8e20c..6edcc9e3 100644 --- a/crates/smtp/src/scripts/event_loop.rs +++ b/crates/smtp/src/scripts/event_loop.rs @@ -178,7 +178,7 @@ impl SMTP { ); } } - Lookup::Directory { .. } => { + Lookup::Directory { .. } | Lookup::Map { .. } => { // Not implemented } } diff --git a/crates/smtp/src/scripts/exec.rs b/crates/smtp/src/scripts/exec.rs index d09b97b3..b961b65c 100644 --- a/crates/smtp/src/scripts/exec.rs +++ b/crates/smtp/src/scripts/exec.rs @@ -23,6 +23,7 @@ use std::{sync::Arc, time::SystemTime}; +use mail_auth::common::resolver::ToReverseName; use sieve::{runtime::Variable, Envelope, Sieve}; use smtp_proto::*; use tokio::{ @@ -42,6 +43,7 @@ impl Session { let (tls_version, tls_cipher) = self.stream.tls_version_and_cipher(); let mut params = ScriptParameters::new() .set_variable("remote_ip", self.data.remote_ip.to_string()) + .set_variable("remote_ip.reverse", self.data.remote_ip.to_reverse_name()) .set_variable("helo_domain", self.data.helo_domain.to_lowercase()) .set_variable( "authenticated_as", diff --git a/crates/smtp/src/scripts/functions/array.rs b/crates/smtp/src/scripts/functions/array.rs index 61093fcd..e9943db5 100644 --- a/crates/smtp/src/scripts/functions/array.rs +++ b/crates/smtp/src/scripts/functions/array.rs @@ -174,7 +174,7 @@ pub fn fn_winnow<'x>(_: &'x Context<'x, SieveContext>, mut v: Vec>) .iter() .filter_map(|i| { if !i.is_empty() { - i.to_owned().into() + i.clone().into() } else { None } diff --git a/crates/smtp/src/scripts/functions/email.rs b/crates/smtp/src/scripts/functions/email.rs index 6997428f..70424579 100644 --- a/crates/smtp/src/scripts/functions/email.rs +++ b/crates/smtp/src/scripts/functions/email.rs @@ -100,67 +100,71 @@ pub fn fn_is_email<'x>(_: &'x Context<'x, SieveContext>, v: Vec>) - pub fn fn_email_part<'x>(_: &'x Context<'x, SieveContext>, v: Vec>) -> Variable<'x> { v[0].transform(|s| { s.rsplit_once('@') - .and_then(|(u, d)| match v[1].to_cow().as_ref() { - "local" => u.trim().into(), - "domain" => d.trim().into(), - _ => None, + .map(|(u, d)| match v[1].to_cow().as_ref() { + "local" => Variable::StringRef(u.trim()), + "domain" => Variable::StringRef(d.trim()), + _ => Variable::default(), }) + .unwrap_or_default() }) } +#[derive(PartialEq, Eq, Clone, Copy)] +enum MatchPart { + Sld, + Tld, + Host, +} + pub fn fn_domain_part<'x>( ctx: &'x Context<'x, SieveContext>, v: Vec>, ) -> Variable<'x> { - #[derive(PartialEq, Eq)] - enum MatchPart { - Sld, - Tld, - Host, - } - let domain = v[0].to_cow(); let match_part = match v[1].to_cow().as_ref() { "sld" => MatchPart::Sld, "tld" => MatchPart::Tld, "host" => MatchPart::Host, _ => return Variable::default(), }; - let d = domain.trim().to_lowercase(); - let mut seen_dot = false; - for (pos, ch) in d.as_bytes().iter().enumerate().rev() { - if *ch == b'.' { - if seen_dot { - let maybe_domain = - std::str::from_utf8(&d.as_bytes()[pos + 1..]).unwrap_or_default(); - if !ctx.context().psl.contains(maybe_domain) { - return if match_part == MatchPart::Sld { - maybe_domain - } else { - std::str::from_utf8(&d.as_bytes()[..pos]).unwrap_or_default() + + v[0].transform(|domain| { + let d = domain.trim().to_lowercase(); + let mut seen_dot = false; + for (pos, ch) in d.as_bytes().iter().enumerate().rev() { + if *ch == b'.' { + if seen_dot { + let maybe_domain = + std::str::from_utf8(&d.as_bytes()[pos + 1..]).unwrap_or_default(); + if !ctx.context().psl.contains(maybe_domain) { + return if match_part == MatchPart::Sld { + maybe_domain + } else { + std::str::from_utf8(&d.as_bytes()[..pos]).unwrap_or_default() + } + .to_string() + .into(); } - .to_string() - .into(); + } else if match_part == MatchPart::Tld { + return std::str::from_utf8(&d.as_bytes()[pos + 1..]) + .unwrap_or_default() + .to_string() + .into(); + } else { + seen_dot = true; } - } else if match_part == MatchPart::Tld { - return std::str::from_utf8(&d.as_bytes()[pos + 1..]) - .unwrap_or_default() - .to_string() - .into(); - } else { - seen_dot = true; } } - } - if seen_dot { - if match_part == MatchPart::Sld { + if seen_dot { + if match_part == MatchPart::Sld { + d.into() + } else { + Variable::default() + } + } else if match_part == MatchPart::Host { d.into() } else { Variable::default() } - } else if match_part == MatchPart::Host { - d.into() - } else { - Variable::default() - } + }) } diff --git a/crates/smtp/src/scripts/functions/html.rs b/crates/smtp/src/scripts/functions/html.rs index 1399e448..874b5294 100644 --- a/crates/smtp/src/scripts/functions/html.rs +++ b/crates/smtp/src/scripts/functions/html.rs @@ -32,13 +32,6 @@ pub fn fn_html_to_text<'x>(_: &'x Context<'x, SieveContext>, v: Vec html_to_text(v[0].to_cow().as_ref()).into() } -pub fn fn_tokenize_html<'x>( - _: &'x Context<'x, SieveContext>, - v: Vec>, -) -> Variable<'x> { - html_to_tokens(v[0].to_cow().as_ref()).into() -} - pub fn fn_html_has_tag<'x>(_: &'x Context<'x, SieveContext>, v: Vec>) -> Variable<'x> { v[0].as_array() .map(|arr| { diff --git a/crates/smtp/src/scripts/functions/misc.rs b/crates/smtp/src/scripts/functions/misc.rs index 147fac22..5ad80087 100644 --- a/crates/smtp/src/scripts/functions/misc.rs +++ b/crates/smtp/src/scripts/functions/misc.rs @@ -21,10 +21,17 @@ * for more details. */ +use std::net::IpAddr; + +use mail_auth::common::resolver::ToReverseName; +use sha1::Sha1; +use sha2::{Sha256, Sha512}; use sieve::{runtime::Variable, Context}; use crate::config::scripts::SieveContext; +use super::ApplyString; + pub fn fn_is_empty<'x>(_: &'x Context<'x, SieveContext>, v: Vec>) -> Variable<'x> { match &v[0] { Variable::String(s) => s.is_empty(), @@ -40,6 +47,76 @@ pub fn fn_is_ip_addr<'x>(_: &'x Context<'x, SieveContext>, v: Vec>) v[0].to_cow().parse::().is_ok().into() } +pub fn fn_is_ipv4_addr<'x>(_: &'x Context<'x, SieveContext>, v: Vec>) -> Variable<'x> { + v[0].to_cow() + .parse::() + .map_or(false, |ip| matches!(ip, IpAddr::V4(_))) + .into() +} + +pub fn fn_is_ipv6_addr<'x>(_: &'x Context<'x, SieveContext>, v: Vec>) -> Variable<'x> { + v[0].to_cow() + .parse::() + .map_or(false, |ip| matches!(ip, IpAddr::V6(_))) + .into() +} + +pub fn fn_ip_reverse_name<'x>( + _: &'x Context<'x, SieveContext>, + v: Vec>, +) -> Variable<'x> { + v[0].to_cow() + .parse::() + .map(|ip| ip.to_reverse_name()) + .unwrap_or_default() + .into() +} + +pub fn fn_detect_file_type<'x>( + ctx: &'x Context<'x, SieveContext>, + v: Vec>, +) -> Variable<'x> { + ctx.message() + .part(ctx.part()) + .and_then(|p| infer::get(p.contents())) + .map(|t| { + Variable::String( + if v[0].to_cow() != "ext" { + t.mime_type() + } else { + t.extension() + } + .to_string(), + ) + }) + .unwrap_or_default() +} + +pub fn fn_hash<'x>(_: &'x Context<'x, SieveContext>, v: Vec>) -> Variable<'x> { + use sha1::Digest; + let hash = v[1].to_cow(); + + v[0].transform(|value| match hash.as_ref() { + "md5" => format!("{:x}", md5::compute(value.as_bytes())).into(), + "sha1" => { + let mut hasher = Sha1::new(); + hasher.update(value.as_bytes()); + format!("{:x}", hasher.finalize()).into() + } + "sha256" => { + let mut hasher = Sha256::new(); + hasher.update(value.as_bytes()); + format!("{:x}", hasher.finalize()).into() + } + "sha512" => { + let mut hasher = Sha512::new(); + hasher.update(value.as_bytes()); + format!("{:x}", hasher.finalize()).into() + } + _ => Variable::default(), + }) +} + pub fn fn_is_var_names<'x>( ctx: &'x Context<'x, SieveContext>, _: Vec>, diff --git a/crates/smtp/src/scripts/functions/mod.rs b/crates/smtp/src/scripts/functions/mod.rs index 3d9d6708..b6632e98 100644 --- a/crates/smtp/src/scripts/functions/mod.rs +++ b/crates/smtp/src/scripts/functions/mod.rs @@ -57,9 +57,6 @@ pub fn register_functions() -> FunctionMap { .with_function("is_uppercase", fn_is_uppercase) .with_function("is_lowercase", fn_is_lowercase) .with_function("has_digits", fn_has_digits) - .with_function("tokenize_words", fn_tokenize_words) - .with_function("tokenize_html", fn_tokenize_html) - .with_function("max_line_len", fn_max_line_len) .with_function("count_spaces", fn_count_spaces) .with_function("count_uppercase", fn_count_uppercase) .with_function("count_lowercase", fn_count_lowercase) @@ -69,6 +66,9 @@ pub fn register_functions() -> FunctionMap { .with_function("is_header_utf8_valid", fn_is_header_utf8_valid) .with_function("img_metadata", fn_img_metadata) .with_function("is_ip_addr", fn_is_ip_addr) + .with_function("is_ipv4_addr", fn_is_ipv4_addr) + .with_function("is_ipv6_addr", fn_is_ipv6_addr) + .with_function("ip_reverse_name", fn_ip_reverse_name) .with_function("winnow", fn_winnow) .with_function("has_zwsp", fn_has_zwsp) .with_function("has_obscured", fn_has_obscured) @@ -76,8 +76,9 @@ pub fn register_functions() -> FunctionMap { .with_function("puny_decode", fn_puny_decode) .with_function("unicode_skeleton", fn_unicode_skeleton) .with_function("cure_text", fn_cure_text) + .with_function("detect_file_type", fn_detect_file_type) .with_function_args("sort", fn_sort, 2) - .with_function_args("tokenize_url", fn_tokenize_url, 2) + .with_function_args("tokenize", fn_tokenize, 2) .with_function_args("email_part", fn_email_part, 2) .with_function_args("domain_part", fn_domain_part, 2) .with_function_args("eq_ignore_case", fn_eq_ignore_case, 2) @@ -97,9 +98,12 @@ pub fn register_functions() -> FunctionMap { .with_function_args("substring", fn_substring, 3) .with_function_args("split", fn_split, 2) .with_function_args("rsplit", fn_rsplit, 2) + .with_function_args("split_once", fn_split_once, 2) + .with_function_args("rsplit_once", fn_rsplit_once, 2) .with_function_args("strip_prefix", fn_strip_prefix, 2) .with_function_args("strip_suffix", fn_strip_suffix, 2) .with_function_args("is_intersect", fn_is_intersect, 2) + .with_function_args("hash", fn_hash, 2) .with_function_no_args("is_encoding_problem", fn_is_encoding_problem) .with_function_no_args("is_attachment", fn_is_attachment) .with_function_no_args("is_body", fn_is_body) @@ -108,36 +112,33 @@ pub fn register_functions() -> FunctionMap { } pub trait ApplyString<'x> { - fn transform(&self, f: impl Fn(&str) -> Option<&str>) -> Variable<'x>; - fn transform_string>>( - &self, - f: impl Fn(&str) -> T, - ) -> Option>; + fn transform(&self, f: impl Fn(&'_ str) -> Variable<'_>) -> Variable<'x>; } impl<'x> ApplyString<'x> for Variable<'x> { - fn transform(&self, f: impl Fn(&str) -> Option<&str>) -> Variable<'x> { + fn transform(&self, f: impl Fn(&'_ str) -> Variable<'_>) -> Variable<'x> { match self { - Variable::String(s) => { - f(s).map_or(Variable::default(), |s| Variable::from(s.to_string())) - } - Variable::StringRef(s) => f(s).map_or(Variable::default(), Variable::from), - v => f(v.to_string().as_str()) - .map_or(Variable::default(), |s| Variable::from(s.to_string())), - } - } - - fn transform_string>>( - &self, - f: impl Fn(&str) -> T, - ) -> Option> { - match self { - Variable::String(s) => Some(f(s).into()), - Variable::StringRef(s) => Some(f(s).into()), - Variable::Integer(_) - | Variable::Float(_) - | Variable::Array(_) - | Variable::ArrayRef(_) => None, + Variable::StringRef(s) => f(s), + Variable::String(s) => f(s).into_owned(), + Variable::ArrayRef(list) => list + .iter() + .map(|v| match v { + Variable::String(s) => f(s), + Variable::StringRef(s) => f(s), + v => f(v.to_cow().as_ref()).into_owned(), + }) + .collect::>() + .into(), + Variable::Array(list) => list + .iter() + .map(|v| match v { + Variable::StringRef(s) => f(s), + Variable::String(s) => f(s).into_owned(), + v => f(v.to_cow().as_ref()).into_owned(), + }) + .collect::>() + .into(), + v => f(v.to_cow().as_ref()).into_owned(), } } } diff --git a/crates/smtp/src/scripts/functions/text.rs b/crates/smtp/src/scripts/functions/text.rs index 947ad5cd..56ebaa65 100644 --- a/crates/smtp/src/scripts/functions/text.rs +++ b/crates/smtp/src/scripts/functions/text.rs @@ -23,20 +23,20 @@ use sieve::{runtime::Variable, Context}; -use crate::config::scripts::SieveContext; +use crate::{config::scripts::SieveContext, scripts::functions::url::tokenize_email}; -use super::ApplyString; +use super::{html::html_to_tokens, url::tokenize_url, ApplyString}; pub fn fn_trim<'x>(_: &'x Context<'x, SieveContext>, v: Vec>) -> Variable<'x> { - v[0].transform(|s| Some(s.trim())) + v[0].transform(|s| Variable::StringRef(s.trim())) } pub fn fn_trim_end<'x>(_: &'x Context<'x, SieveContext>, v: Vec>) -> Variable<'x> { - v[0].transform(|s| Some(s.trim_end())) + v[0].transform(|s| Variable::StringRef(s.trim_end())) } pub fn fn_trim_start<'x>(_: &'x Context<'x, SieveContext>, v: Vec>) -> Variable<'x> { - v[0].transform(|s| Some(s.trim_start())) + v[0].transform(|s| Variable::StringRef(s.trim_start())) } pub fn fn_len<'x>(_: &'x Context<'x, SieveContext>, v: Vec>) -> Variable<'x> { @@ -50,79 +50,38 @@ pub fn fn_len<'x>(_: &'x Context<'x, SieveContext>, v: Vec>) -> Var .into() } -pub fn fn_to_lowercase<'x>( - _: &'x Context<'x, SieveContext>, - mut v: Vec>, -) -> Variable<'x> { - match v.remove(0) { - Variable::String(s) => s.to_lowercase().into(), - Variable::StringRef(s) => s.to_lowercase().into(), - Variable::Array(a) => a - .iter() - .map(|v| v.to_cow().to_lowercase().into()) - .collect::>() - .into(), - Variable::ArrayRef(a) => a - .iter() - .map(|v| v.to_cow().to_lowercase().into()) - .collect::>() - .into(), - v => v, - } +pub fn fn_to_lowercase<'x>(_: &'x Context<'x, SieveContext>, v: Vec>) -> Variable<'x> { + v[0].transform(|s| Variable::String(s.to_lowercase())) } -pub fn fn_to_uppercase<'x>( - _: &'x Context<'x, SieveContext>, - mut v: Vec>, -) -> Variable<'x> { - match v.remove(0) { - Variable::String(s) => s.to_uppercase().into(), - Variable::StringRef(s) => s.to_uppercase().into(), - Variable::Array(a) => a - .iter() - .map(|v| v.to_cow().to_uppercase().into()) - .collect::>() - .into(), - Variable::ArrayRef(a) => a - .iter() - .map(|v| v.to_cow().to_uppercase().into()) - .collect::>() - .into(), - v => v, - } +pub fn fn_to_uppercase<'x>(_: &'x Context<'x, SieveContext>, v: Vec>) -> Variable<'x> { + v[0].transform(|s| Variable::String(s.to_uppercase())) } pub fn fn_is_uppercase<'x>(_: &'x Context<'x, SieveContext>, v: Vec>) -> Variable<'x> { - v[0].to_cow() - .as_ref() - .chars() - .filter(|c| c.is_alphabetic()) - .all(|c| c.is_uppercase()) - .into() + v[0].transform(|s| { + s.chars() + .filter(|c| c.is_alphabetic()) + .all(|c| c.is_uppercase()) + .into() + }) } pub fn fn_is_lowercase<'x>(_: &'x Context<'x, SieveContext>, v: Vec>) -> Variable<'x> { - v[0].to_cow() - .as_ref() - .chars() - .filter(|c| c.is_alphabetic()) - .all(|c| c.is_lowercase()) - .into() + v[0].transform(|s| { + s.chars() + .filter(|c| c.is_alphabetic()) + .all(|c| c.is_lowercase()) + .into() + }) } pub fn fn_has_digits<'x>(_: &'x Context<'x, SieveContext>, v: Vec>) -> Variable<'x> { - v[0].to_cow() - .as_ref() - .chars() - .any(|c| c.is_ascii_digit()) - .into() + v[0].transform(|s| s.chars().any(|c| c.is_ascii_digit()).into()) } -pub fn fn_tokenize_words<'x>( - _: &'x Context<'x, SieveContext>, - v: Vec>, -) -> Variable<'x> { - match &v[0] { +pub fn tokenize_words<'x>(v: &Variable<'x>) -> Variable<'x> { + match v { Variable::StringRef(s) => s .split_whitespace() .filter(|word| word.chars().all(|c| c.is_alphanumeric())) @@ -143,15 +102,18 @@ pub fn fn_tokenize_words<'x>( .into() } -pub fn fn_max_line_len<'x>(_: &'x Context<'x, SieveContext>, v: Vec>) -> Variable<'x> { - match &v[0] { - Variable::String(s) => s.lines().map(|l| l.len()).max().unwrap_or(0), - Variable::StringRef(s) => s.lines().map(|l| l.len()).max().unwrap_or(0), - Variable::Integer(_) | Variable::Float(_) => 0, - Variable::Array(a) => a.iter().map(|v| v.to_cow().len()).max().unwrap_or(0), - Variable::ArrayRef(a) => a.iter().map(|v| v.to_cow().len()).max().unwrap_or(0), +pub fn fn_tokenize<'x>( + ctx: &'x Context<'x, SieveContext>, + mut v: Vec>, +) -> Variable<'x> { + match v[1].to_cow().as_ref() { + "html" => html_to_tokens(v[0].to_cow().as_ref()).into(), + "words" => tokenize_words(&v[0]), + "uri" | "url" => tokenize_url(ctx, v.remove(0), false), + "uri_strict" | "url_strict" => tokenize_url(ctx, v.remove(0), true), + "email" => tokenize_email(v.remove(0)), + _ => Variable::default(), } - .into() } pub fn fn_count_spaces<'x>(_: &'x Context<'x, SieveContext>, v: Vec>) -> Variable<'x> { @@ -265,12 +227,20 @@ pub fn fn_substring<'x>(_: &'x Context<'x, SieveContext>, v: Vec>) pub fn fn_strip_prefix<'x>(_: &'x Context<'x, SieveContext>, v: Vec>) -> Variable<'x> { let prefix = v[1].to_cow(); - v[0].transform(|s| s.strip_prefix(prefix.as_ref())) + v[0].transform(|s| { + s.strip_prefix(prefix.as_ref()) + .map(Variable::StringRef) + .unwrap_or_default() + }) } pub fn fn_strip_suffix<'x>(_: &'x Context<'x, SieveContext>, v: Vec>) -> Variable<'x> { let suffix = v[1].to_cow(); - v[0].transform(|s| s.strip_suffix(suffix.as_ref())) + v[0].transform(|s| { + s.strip_suffix(suffix.as_ref()) + .map(Variable::StringRef) + .unwrap_or_default() + }) } pub fn fn_split<'x>(_: &'x Context<'x, SieveContext>, v: Vec>) -> Variable<'x> { @@ -315,6 +285,62 @@ pub fn fn_rsplit<'x>(_: &'x Context<'x, SieveContext>, v: Vec>) -> } } +pub fn fn_split_once<'x>(_: &'x Context<'x, SieveContext>, v: Vec>) -> Variable<'x> { + match &v[0] { + Variable::StringRef(s) => s + .split_once(v[1].to_cow().as_ref()) + .map(|(a, b)| Variable::Array(vec![Variable::StringRef(a), Variable::StringRef(b)])) + .unwrap_or_default(), + Variable::String(s) => s + .split_once(v[1].to_cow().as_ref()) + .map(|(a, b)| { + Variable::Array(vec![ + Variable::String(a.to_string()), + Variable::String(b.to_string()), + ]) + }) + .unwrap_or_default(), + val => val + .to_string() + .split_once(v[1].to_cow().as_ref()) + .map(|(a, b)| { + Variable::Array(vec![ + Variable::String(a.to_string()), + Variable::String(b.to_string()), + ]) + }) + .unwrap_or_default(), + } +} + +pub fn fn_rsplit_once<'x>(_: &'x Context<'x, SieveContext>, v: Vec>) -> Variable<'x> { + match &v[0] { + Variable::StringRef(s) => s + .rsplit_once(v[1].to_cow().as_ref()) + .map(|(a, b)| Variable::Array(vec![Variable::StringRef(a), Variable::StringRef(b)])) + .unwrap_or_default(), + Variable::String(s) => s + .rsplit_once(v[1].to_cow().as_ref()) + .map(|(a, b)| { + Variable::Array(vec![ + Variable::String(a.to_string()), + Variable::String(b.to_string()), + ]) + }) + .unwrap_or_default(), + val => val + .to_string() + .rsplit_once(v[1].to_cow().as_ref()) + .map(|(a, b)| { + Variable::Array(vec![ + Variable::String(a.to_string()), + Variable::String(b.to_string()), + ]) + }) + .unwrap_or_default(), + } +} + /** * `levenshtein-rs` - levenshtein * diff --git a/crates/smtp/src/scripts/functions/url.rs b/crates/smtp/src/scripts/functions/url.rs index 94c69763..aaf86036 100644 --- a/crates/smtp/src/scripts/functions/url.rs +++ b/crates/smtp/src/scripts/functions/url.rs @@ -24,19 +24,22 @@ use std::net::IpAddr; use hyper::Uri; +use linkify::LinkKind; use sieve::{runtime::Variable, Context}; use crate::config::scripts::SieveContext; -pub fn fn_tokenize_url<'x>( - ctx: &'x Context<'x, SieveContext>, - mut v: Vec>, -) -> Variable<'x> { - let must_have_scheme = v[1].to_bool(); +use super::ApplyString; - match v.remove(0) { +pub fn tokenize_url<'x>( + ctx: &'x Context<'x, SieveContext>, + v: Variable<'x>, + must_have_scheme: bool, +) -> Variable<'x> { + match v { Variable::StringRef(text) => linkify::LinkFinder::new() .url_must_have_scheme(must_have_scheme) + .kinds(&[LinkKind::Url]) .links(text.as_ref()) .filter_map(|url| filter_url(url.as_str(), must_have_scheme, ctx)) .collect::>() @@ -44,6 +47,7 @@ pub fn fn_tokenize_url<'x>( v @ (Variable::String(_) | Variable::Array(_) | Variable::ArrayRef(_)) => { linkify::LinkFinder::new() .url_must_have_scheme(must_have_scheme) + .kinds(&[LinkKind::Url]) .links(v.to_cow().as_ref()) .filter_map(|url| { filter_url(url.as_str(), must_have_scheme, ctx).map(|v| v.into_owned()) @@ -55,6 +59,28 @@ pub fn fn_tokenize_url<'x>( } } +pub fn tokenize_email(v: Variable<'_>) -> Variable<'_> { + match v { + Variable::StringRef(text) => linkify::LinkFinder::new() + .email_domain_must_have_dot(true) + .kinds(&[LinkKind::Email]) + .links(text.as_ref()) + .map(|email| Variable::StringRef(email.as_str())) + .collect::>() + .into(), + v @ (Variable::String(_) | Variable::Array(_) | Variable::ArrayRef(_)) => { + linkify::LinkFinder::new() + .email_domain_must_have_dot(true) + .kinds(&[LinkKind::Email]) + .links(v.to_cow().as_ref()) + .map(|email| Variable::String(email.as_str().to_string())) + .collect::>() + .into() + } + v => v, + } +} + fn filter_url<'x, 'y>( url: &'x str, must_have_scheme: bool, @@ -84,51 +110,51 @@ fn filter_url<'x, 'y>( } pub fn fn_uri_part<'x>(_: &'x Context<'x, SieveContext>, v: Vec>) -> Variable<'x> { - v[0].to_cow() - .parse::() - .ok() - .and_then(|uri| match v[1].to_cow().as_ref() { - "scheme" => uri.scheme_str().map(|s| Variable::String(s.to_lowercase())), - "host" => uri.host().map(|s| Variable::String(s.to_lowercase())), - "scheme_host" => uri - .scheme_str() - .and_then(|s| (s, uri.host()?).into()) - .map(|(s, h)| Variable::String(format!("{}://{}", s, h))), - "path" => Variable::String(uri.path().to_string()).into(), - "port" => uri.port_u16().map(|port| Variable::Integer(port as i64)), - "query" => uri.query().map(|s| Variable::String(s.to_string())), - "path_query" => uri - .path_and_query() - .map(|s| Variable::String(s.to_string())), - "authority" => uri.authority().map(|s| Variable::String(s.to_string())), - _ => None, - }) - .unwrap_or_default() + let part = v[1].to_cow(); + v[0].transform(|uri| { + uri.parse::() + .ok() + .and_then(|uri| match part.as_ref() { + "scheme" => uri.scheme_str().map(|s| Variable::String(s.to_string())), + "host" => uri.host().map(|s| Variable::String(s.to_string())), + "scheme_host" => uri + .scheme_str() + .and_then(|s| (s, uri.host()?).into()) + .map(|(s, h)| Variable::String(format!("{}://{}", s, h))), + "path" => Variable::String(uri.path().to_string()).into(), + "port" => uri.port_u16().map(|port| Variable::Integer(port as i64)), + "query" => uri.query().map(|s| Variable::String(s.to_string())), + "path_query" => uri + .path_and_query() + .map(|s| Variable::String(s.to_string())), + "authority" => uri.authority().map(|s| Variable::String(s.to_string())), + _ => None, + }) + .unwrap_or_default() + }) } -pub fn fn_puny_decode<'x>( - _: &'x Context<'x, SieveContext>, - mut v: Vec>, -) -> Variable<'x> { - let domain = v[0].to_cow(); - if domain.contains("xn--") { - let mut decoded = String::with_capacity(domain.len()); - for part in domain.split('.') { - if !decoded.is_empty() { - decoded.push('.'); - } +pub fn fn_puny_decode<'x>(_: &'x Context<'x, SieveContext>, v: Vec>) -> Variable<'x> { + v[0].transform(|domain| { + if domain.contains("xn--") { + let mut decoded = String::with_capacity(domain.len()); + for part in domain.split('.') { + if !decoded.is_empty() { + decoded.push('.'); + } - if let Some(puny) = part - .strip_prefix("xn--") - .and_then(idna::punycode::decode_to_string) - { - decoded.push_str(&puny); - } else { - decoded.push_str(part); + if let Some(puny) = part + .strip_prefix("xn--") + .and_then(idna::punycode::decode_to_string) + { + decoded.push_str(&puny); + } else { + decoded.push_str(part); + } } + decoded.into() + } else { + domain.into() } - decoded.into() - } else { - v.remove(0) - } + }) } diff --git a/crates/smtp/src/scripts/plugins/dns.rs b/crates/smtp/src/scripts/plugins/dns.rs index 057189d1..d938be6d 100644 --- a/crates/smtp/src/scripts/plugins/dns.rs +++ b/crates/smtp/src/scripts/plugins/dns.rs @@ -23,7 +23,7 @@ use std::net::IpAddr; -use mail_auth::IpLookupStrategy; +use mail_auth::{Error, IpLookupStrategy}; use sieve::{runtime::Variable, FunctionMap}; use crate::config::scripts::SieveContext; @@ -71,6 +71,21 @@ pub fn exec(ctx: PluginContext<'_>) -> Variable<'static> { .into(), Err(err) => err.short_error().into(), } + } else if record_type.eq_ignore_ascii_case("txt") { + #[cfg(feature = "test_mode")] + { + if entry.contains("origin") { + return Variable::String("23028|US|arin|2002-01-04".to_string()); + } + } + + match ctx + .handle + .block_on(ctx.core.resolvers.dns.txt_raw_lookup(entry.as_ref())) + { + Ok(result) => Variable::String(String::from_utf8(result).unwrap_or_default()), + Err(err) => err.short_error().into(), + } } else if record_type.eq_ignore_ascii_case("ptr") { if let Ok(addr) = entry.parse::() { match ctx.handle.block_on(ctx.core.resolvers.dns.ptr_lookup(addr)) { @@ -85,6 +100,14 @@ pub fn exec(ctx: PluginContext<'_>) -> Variable<'static> { Variable::default() } } else if record_type.eq_ignore_ascii_case("ipv4") { + #[cfg(feature = "test_mode")] + { + if entry.contains(".168.192.") { + let parts = entry.split('.').collect::>(); + return vec![Variable::String(format!("127.0.{}.{}", parts[1], parts[0]))].into(); + } + } + match ctx .handle .block_on(ctx.core.resolvers.dns.ipv4_lookup(entry.as_ref())) @@ -123,44 +146,56 @@ pub fn exec_exists(ctx: PluginContext<'_>) -> Variable<'static> { IpLookupStrategy::Ipv4thenIpv6, 10, )) { - Ok(result) => !result.is_empty(), - Err(_) => false, + Ok(result) => i64::from(!result.is_empty()), + Err(Error::DnsRecordNotFound(_)) => 0, + Err(_) => -1, } } else if record_type.eq_ignore_ascii_case("mx") { match ctx .handle .block_on(ctx.core.resolvers.dns.mx_lookup(entry.as_ref())) { - Ok(result) => result.iter().any(|mx| !mx.exchanges.is_empty()), - Err(_) => false, + Ok(result) => i64::from(result.iter().any(|mx| !mx.exchanges.is_empty())), + Err(Error::DnsRecordNotFound(_)) => 0, + Err(_) => -1, } } else if record_type.eq_ignore_ascii_case("ptr") { if let Ok(addr) = entry.parse::() { match ctx.handle.block_on(ctx.core.resolvers.dns.ptr_lookup(addr)) { - Ok(result) => !result.is_empty(), - Err(_) => false, + Ok(result) => i64::from(!result.is_empty()), + Err(Error::DnsRecordNotFound(_)) => 0, + Err(_) => -1, } } else { - false + -1 } } else if record_type.eq_ignore_ascii_case("ipv4") { + #[cfg(feature = "test_mode")] + { + if entry.starts_with("2.0.168.192.") { + return 1.into(); + } + } + match ctx .handle .block_on(ctx.core.resolvers.dns.ipv4_lookup(entry.as_ref())) { - Ok(result) => !result.is_empty(), - Err(_) => false, + Ok(result) => i64::from(!result.is_empty()), + Err(Error::DnsRecordNotFound(_)) => 0, + Err(_) => -1, } } else if record_type.eq_ignore_ascii_case("ipv6") { match ctx .handle .block_on(ctx.core.resolvers.dns.ipv6_lookup(entry.as_ref())) { - Ok(result) => !result.is_empty(), - Err(_) => false, + Ok(result) => i64::from(!result.is_empty()), + Err(Error::DnsRecordNotFound(_)) => 0, + Err(_) => -1, } } else { - false + -1 } .into() } diff --git a/crates/smtp/src/scripts/plugins/lookup.rs b/crates/smtp/src/scripts/plugins/lookup.rs index af8eb23d..3a677349 100644 --- a/crates/smtp/src/scripts/plugins/lookup.rs +++ b/crates/smtp/src/scripts/plugins/lookup.rs @@ -31,6 +31,10 @@ pub fn register(plugin_id: u32, fnc_map: &mut FunctionMap) { fnc_map.set_external_function("lookup", plugin_id, 2); } +pub fn register_map(plugin_id: u32, fnc_map: &mut FunctionMap) { + fnc_map.set_external_function("lookup_map", plugin_id, 2); +} + pub fn exec(ctx: PluginContext<'_>) -> Variable<'static> { let lookup_id = ctx.arguments[0].to_cow(); let item = ctx.arguments[1].to_cow(); @@ -56,3 +60,28 @@ pub fn exec(ctx: PluginContext<'_>) -> Variable<'static> { false.into() } + +pub fn exec_map(ctx: PluginContext<'_>) -> Variable<'static> { + let lookup_id = ctx.arguments[0].to_cow(); + let item = ctx.arguments[1].to_cow(); + let span = ctx.span; + + if !lookup_id.is_empty() && !item.is_empty() { + if let Some(lookup) = ctx.core.sieve.lookup.get(lookup_id.as_ref()) { + return ctx + .handle + .block_on(lookup.lookup(item.as_ref())) + .unwrap_or_default(); + } else { + tracing::warn!( + parent: span, + context = "sieve:lookup", + event = "failed", + reason = "Unknown lookup id", + lookup_id = %lookup_id, + ); + } + } + + Variable::default() +} diff --git a/crates/smtp/src/scripts/plugins/mod.rs b/crates/smtp/src/scripts/plugins/mod.rs index 0ccb2171..e5b99452 100644 --- a/crates/smtp/src/scripts/plugins/mod.rs +++ b/crates/smtp/src/scripts/plugins/mod.rs @@ -46,18 +46,20 @@ pub struct PluginContext<'x> { pub arguments: Vec>, } -const PLUGINS_EXEC: [ExecPluginFnc; 6] = [ +const PLUGINS_EXEC: [ExecPluginFnc; 7] = [ query::exec, exec::exec, lookup::exec, + lookup::exec_map, dns::exec, dns::exec_exists, http::exec_header, ]; -const PLUGINS_REGISTER: [RegisterPluginFnc; 6] = [ +const PLUGINS_REGISTER: [RegisterPluginFnc; 7] = [ query::register, exec::register, lookup::register, + lookup::register_map, dns::register, dns::register_exists, http::register_header, diff --git a/crates/smtp/src/scripts/plugins/query.rs b/crates/smtp/src/scripts/plugins/query.rs index 19dafaea..d5d3447a 100644 --- a/crates/smtp/src/scripts/plugins/query.rs +++ b/crates/smtp/src/scripts/plugins/query.rs @@ -21,11 +21,8 @@ * for more details. */ -use directory::QueryColumn; -use sieve::{runtime::Variable, FunctionMap}; -use smtp_proto::IntoString; - use crate::config::scripts::SieveContext; +use sieve::{runtime::Variable, FunctionMap}; use super::PluginContext; @@ -73,23 +70,15 @@ pub fn exec(ctx: PluginContext<'_>) -> Variable<'static> { .get(..6) .map_or(false, |q| q.eq_ignore_ascii_case(b"SELECT")) { - if let Ok(query_columns) = ctx.handle.block_on(directory.query( + if let Ok(mut query_columns) = ctx.handle.block_on(directory.query( &query, ¶meters.iter().map(String::as_str).collect::>(), )) { - let mut result = Vec::with_capacity(query_columns.len()); - for column in query_columns { - result.push(match column { - QueryColumn::Integer(v) => Variable::Integer(v), - QueryColumn::Bool(v) => Variable::Integer(i64::from(v)), - QueryColumn::Float(v) => Variable::Float(v), - QueryColumn::Text(v) => Variable::String(v), - QueryColumn::Blob(v) => Variable::String(v.into_string()), - QueryColumn::Null => Variable::StringRef(""), - }); + match query_columns.len() { + 1 => query_columns.pop().map(Variable::from).unwrap(), + 0 => Variable::default(), + _ => Variable::Array(query_columns.into_iter().map(Variable::from).collect()), } - - result.into() } else { false.into() } diff --git a/resources/config/lists/disposable.txt.gz b/resources/config/lists/disposable.txt.gz deleted file mode 100644 index 17db272e..00000000 Binary files a/resources/config/lists/disposable.txt.gz and /dev/null differ diff --git a/resources/config/lists/dmarc_whitelist_new.inc b/resources/config/lists/dmarc_whitelist_new.inc deleted file mode 100644 index 5dddf218..00000000 --- a/resources/config/lists/dmarc_whitelist_new.inc +++ /dev/null @@ -1,792 +0,0 @@ - -# Domains with valid DMARC and DKIM -# These domains come from some top visited sites that have restrictive dmarc policy -# This file is intended for Rspamd version 1.8.1+ -# Keep sorted when editing to simplify tracks - -18f.gov -1password.com -2gis.com -4chan.org -4pda.ru -9-11commission.gov -911.gov -aberdeenshire.gov.uk -abilityone.gov -absolutbank.ru -access-board.gov -acquisition.gov -acus.gov -ada.gov -adf.gov -adidas.co.kr -adidas.com.au -adidas.com.br -adidas.com.hk -adidas.fi -adidas.fr -admongo.gov -adobe.dk -adobe.es -adobeawards.com -adp.com both:1.0 -advice.hmrc.gov.uk -aerocivil.gov.co -aerocivil.gov.co -afreximbank.com -agingstats.gov -agro.ru -ahcpr.gov -aids.gov -airbnb.co.uk -airbnb.com -airbnb.com.tr -airbnb.cz -airbnb.de -airbnb.fi -airbnb.fr -airbnb.pl -airbnb.ru -airbnb.se -airnow.gov -airtel.in -alfabank.com -alfabank.ru both:1.0 -alfastrah.ru -alibaba.com -aliexpress.com bl:1.0 -alipay.com -alkupone.ru -alzheimers.gov -amazon.co.uk both:1.0 -amazon.com both:1.0 -amazon.com.br both:1.0 -amberalert.gov -americanexpress.com both:1.0 -ameslab.gov -angus.gov.uk -anidub.com -annapolis.gov -anstaskforce.gov -apple.com.au -apple.com.cn -apps.gov -archives.gov -arctic.gov -arionbanki.is -asg.com -asic.gov.au -askona.ru -asos.com -assist.ru -atf.gov -avast.com -avg.com -avito.ru -avtoradio.ru -axisbank.com -badoo.com bl:1.0 -baltbank.ru -bank.lv -banki.ru -bankofamerica.com -barclaycard.co.uk both:1.0 -barclays.co.uk both:1.0 -barclays.com both:1.0 -battle.net -beeline.kz -beeline.ru -benefits.gov -betfaq.ru -biglion.ru -binary.com -binbank.ru -bioethics.gov -biometrics.gov -biopreferred.gov -birminghampost.net -bishopsmove.com -bitbank.cc -bjs.gov -blizzard.com -blog.gov.uk -bls.gov -bluestarindia.com -boemre.gov -bolsover.gov.uk -bolton.gov.uk -booking.com -bookmate.com -books.ru -bournemouth.gov.uk -box.com -bpa.gov -brandshop.ru -bridgend.gov.uk -brighton-hove.gov.uk -britishembassy.gov.uk -broadbandmap.gov -bromley.gov.uk -bts.gov -business.gov -caerphilly.gov.uk -caixa.gov.br both:1.0 -cambridge-news.co.uk -campaign.gov.uk -cancer.gov -cannockchasedc.gov.uk -capitalone.co.uk -cardiff.gov.uk -carecredit.com -cbp.gov -cdfifund.gov -centralbedfordshire.gov.uk -ceredigion.gov.uk -cesg.gov.uk -cfda.gov -cfo.gov -challenge.gov -change.org -chase.com -chcoc.gov -childstats.gov -cio.gov -circle.com -citibank.ae -citibank.co.in -citibank.co.uk -citibank.com.my -citibank.hu -citibank.pl -cloud.gov -cloudflare.com -cms.gov -co-operativebank.co.uk -colgate.com.br -collegedrinkingprevention.gov -companies-house.gov.uk -comuneap.gov.it -conab.gov.br both:1.0 -concerts.com -consultant.ru -contact-sys.com -copeland.gov.uk -cosla.gov.uk -courtservice.gov.uk -coventry.gov.uk -cre.gov.uk -csosa.gov -cuidadodesalud.gov -culturarecreacionydeporte.gov.co -customs.gov.my -customs.gov.ua -cybercrime.gov -dailypost.co.uk -danskebank.dk -danskebank.fi -danskebank.ie -danskebank.no -dartford.gov.uk -dartmoor.gov.uk -dataprotection.gov.uk -daventrydc.gov.uk -dellin.ru -denbighshire.gov.uk -deutsche-bank.de -deutschebank.be -deutschebank.co.in -dh.gov.uk -dhl.com -dhs.gov -diablo3.com -digital.gov -digitalgov.gov -digitalliteracy.gov -disability.gov -disability.gov.uk -dnfsb.gov -docker.com -docusign.net -doe.gov -doioig.gov -dol.gov -doleta.gov -domofond.ru -drought.gov -drugabuse.gov -dsns.gov.ua -dtv.gov -dudley.gov.uk -dyslexiaida.org -e-boks.dk -e-verify.gov -eastdunbarton.gov.uk -eaststaffsbc.gov.uk -eastsuffolk.gov.uk -ebay.be -ebay.ca -ebay.ch -ebay.co.uk both:1.0 -ebay.com -ebay.com.au -ebay.com.cn -ebay.de -ebay.es -ebay.eu -ebay.fr -ebay.in -ebay.it -ebay.se -economy.gov.tr -econsumer.gov -ed.gov -eftps.gov -ehsni.gov.uk -eia.gov -ejob.gov.tw -elance.com -eldorado.ru -email-ee.co.uk -email.tektorg.ru -emarsys.com -ems.gov -energystar.gov -erewash.gov.uk -esetnod32.ru -essex-fire.gov.uk -eubank.kz -evernote.com -everychildmatters.gov.uk -evus.gov -exist.ru -expediamail.com -facebook.com -facebookmail.com -fbi.gov -fcc.gov both:1.0 -fco.gov.uk -fdic.gov -feb.gov -federalreserve.gov -fedex.com -fedramp.gov -fedshirevets.gov -feedthefuture.gov -fema.gov -ferc.gov -fhfa.gov -fhfaoig.gov -fife.gov.uk -financialresearch.gov -financialstability.gov -firstbankpb.bank -firstbankpb.com -firstnet.gov -firstresponder.gov -fishwatch.gov -fitness.gov -flagma.ua -flamp.ru -fletc.gov -fmc.gov both:1.0 -fmcs.gov both:1.0 -foia.gov -food.gov.uk -force.com -fordlibrarymuseum.gov -foreignassistance.gov -foreigntrade.gov.tr -franklinwi.gov -ftc.gov -ftccomplaintassistant.gov -gamereactor.dk -gap.com -garant.ru -geekbrains.ru -geektimes.ru -getsmartaboutdrugs.gov -gibraltar.gov.uk -gitlab.com -globalentry.gov -globalhealth.gov -globe.gov -gloucestershire.gov.uk -goes-r.gov -gosuslugi.ru -gov.uk both:1.0 -groupon.es -groupon.hk -groupon.it -gsa.gov -gsaadvantage.gov -gsaauctions.gov -gsaig.gov -gtbank.com -guideline.gov -guidelines.gov -gwynedd.gov.uk -habr.com -hambleton.gov.uk -harp.gov -hawaiicounty.gov -hdfcbank.com -hdrezka.ag -healthcare.gov -healthypeople.gov -hertfordshire.gov.uk -hh.kz -hh.ru -highland.gov.uk -highwaycode.gov.uk -hillingdon.gov.uk -hiv.gov -hmrc.gov.uk -homeoffice.gov.uk -homesales.gov -hotels.com -hounslow.gov.uk -howto.gov -hru.gov -huduser.gov -hurricanes.gov -iba.gov.au -ice.gov -idmanagement.gov -ikea.ch -ikea.co.uk -ikea.com -ikea.de -ikea.fr -ikea.gr -ikea.nl -ikea.pl -imgur.com -incometaxindiaefiling.gov.in -ing.com -inl.gov -inlandrevenue.gov.uk -insider.co.uk -insolvency.gov.uk -instagram.com -insurekidsnow.gov -invasivespeciesinfo.gov -investor.gov -irda.gov.in -itunes.com -jccbi.gov -jd.ru -jet.com -jimmycarterlibrary.gov -job.com -johnsonsbaby.co.uk -joybuy.com -jpmorgan.com -jpmorgansecurities.com -judiciary.gov.uk -justice.gov -justice.gov.az -justice.gov.uk -jyskebank.dk -kassy.ru -kent.gov.uk -keys.openpgp.org -kids.gov -kingston.gov.uk -kivra.com -klarna.com -klarna.se -kpk.gov.pl -lacoast.gov -landsbanki.is -lanl.gov -lbhf.gov.uk -lcd.gov.uk -learningcurve.gov.uk -leeds.gov.uk -leroymerlin.es -lichfielddc.gov.uk -lincoln.gov.uk -lincolnshire.gov.uk -linkedin.com -livejournal.com -llnl.gov -lloydsbank.com -locatorplus.gov -lostfilm.tv -louisvilleco.gov -love.ru -lufthansa-group.com -lufthansa.com -mackeeper.com -mailgun.net bl:1.0 -mak.com -mandtbank.com -mcc.gov -mcga.gov.uk -mchenrycountyil.gov -mecknc.gov -mediamarkt.se -medicaid.gov -medicare.gov -medium.com -megafon.ru -megaplan.ru -mercadolibre.com.ar -mercadolivre.com.br -merseyfire.gov.uk -merthyr.gov.uk -meshok.ru -messenger.com -microsoft.net -middlesbrough.gov.uk -midlothian.gov.uk -mil.ru -mincit.gov.co -minhacienda.gov.co -minsvyaz.ru -mintic.gov.co -mirrorfootball.co.uk -mkb.ru -mlg.ru -mlg.tv -mns.gov.ua -mod.gov.az -molisa.gov.vn -mos.ru -mosoblbank.ru -mosreg.ru -motinfo.gov.uk -movavi.com -msha.gov -mspb.gov -msport.gov.pl -murfreesborotn.gov -mvideo.ru -mxtoolbox.com -mymoney.gov -myplate.gov -myra.gov -myshared.ru -n-kesteven.gov.uk -n-somerset.gov.uk -nads.gov.ua -nalog.ru -namus.gov -nasa.gov -nationalarchives.gov.uk -nationalservice.gov -nationsreportcard.gov -nbr.gov.bd -nbtbank.com -ncifcrf.gov -ncpw.gov -nctb.gov.bd -ne-derbyshire.gov.uk -nea.gov -nelincs.gov.uk -neobux.com -neolane.net -netflix.com -newegg.com -newmoney.gov -nga.gov -ngu.gov.ua -nhtsa.gov -nic.ru -nidw.gov.bd -nij.gov -nio.gov.uk -niscc.gov.uk -nist.gov -nixonlibrary.gov -nkh.gov.hu -noaa.gov -nordea.dk -nordea.com -nordea.fi -nordea.no -nordea.se -north-ayrshire.gov.uk -north-norfolk.gov.uk -northlincs.gov.uk -norwich.gov.uk -notifications.service.gov.uk -nottinghamcity.gov.uk -nrc-gateway.gov -nrc.gov -nrel.gov -nsf.gov -nsopr.gov -nsopw.gov -nwtrb.gov -oculus.com -ofcm.gov -office.com -officemag.ru -ok.ru -omb.gov -ombudsman.gov.tr -onedrive.com -onguardonline.gov -opengl.org -openinternet.gov -ordsvy.gov.uk -ornl.gov -oshrc.gov -osti.gov -oxfordshire.gov.uk -ozon.ru -paauditor.gov -paccar.com -paddle8.com -pandemicflu.gov -passport.gov.uk -payeer.com -paymentaccuracy.gov -paypal-community.com -paypal.be both:1.0 -paypal.cn both:1.0 -paypal.co.il both:1.0 -paypal.co.uk both:1.0 -paypal.com both:1.0 -paypal.com.au both:1.0 -paypal.com.br both:1.0 -paypal.com.mx both:1.0 -paypal.de both:1.0 -paypal.dk both:1.0 -paypal.es both:1.0 -paypal.fr both:1.0 -paypal.nl both:1.0 -paypal.se both:1.0 -pbgc.gov -pc.gov.au -pch.com -penanghill.gov.my -pepfar.gov -performance.gov -pinterest.co.kr -pinterest.com -pinterest.de -pinterest.jp -pinterest.se -pkc.gov.uk -planeta.ru -platron.ru -plymouth.gov.uk -pm.gov.uk -pmf.gov -pmi.gov -pncbank.com -pokerstars.com -pokerstars.fr -pokerstars.it -pokerstars.net -priorbank.by -privatbank.ua -prospertx.gov -prostocash.com -provident.bank -psbank.ru -psc.gov -punjab.gov.in -puzzle-english.com -qiwi.com -qiwi.ru -rabota.ru -rbkc.gov.uk -ready.gov -reaganlibrary.gov -redbridge.gov.uk -reddit.com -reebok.es -reebok.nl -reginfo.gov -regulations.gov -reisebank.de -renfrewshire.gov.uk -rentonwa.gov -reportband.gov -rgs.ru -richmond.gov.uk -rivers.gov -rkn.gov.ru -ros.gov.uk -roseltorg.ru -rostelecom.ru -roundrocktexas.gov -royalmail.com -rozetka.com.ua -rt.com -rt.ru -rushcliffe.gov.uk -rutubeinfo.ru -sacn.gov.uk -safercar.gov -samhsa.gov -sanmarcostx.gov -sberbank.ru -sberbank-ast.ru -sbir.gov -sbis.ru -scality.com -scdhhs.gov -science360.gov -sciencebase.gov -scijinks.gov -sec.gov -secretservice.gov -section508.gov -semnan.ac.ir -senate.gov -sendgrid.net bl:1 -seniorcorps.gov -serpro.gov.br both:1.0 -service.gov.uk -sftool.gov -shetland.gov.uk -shropshire-cc.gov.uk -shutterstock.com -sigtarp.gov -sk.ru -skat.dk -skatteverket.se -skbbank.ru -skittles.com -skydio.com -skype.com -slideshare.com -smart.gov -smida.gov.ua -smokefree.gov -snickers.com -solardecathlon.gov -sourceforge.net -south-ayrshire.gov.uk -sovest.ru -spbrealty.ru -sportmaster.ru -squarespace.com -sravni.ru -srs.gov -staffordbc.gov.uk -stat.gov.az -stedmundsbury.gov.uk -sthelens.gov.uk -stihl.ru -stopalcoholabuse.gov -stopfraud.gov -studentloans.gov -subscribe.ru -suffolkcc.gov.uk -suncorpbank.com.au -sundaymirror.co.uk -sunlight.net -superjob.ru -surestart.gov.uk -sutton.gov.uk -swansea.gov.uk -swift.com -symantec.com -synologynotification.com -taobao.com -tatar.ru -tauntondeane.gov.uk -tda.gov.uk -tdk.gov.tr -tdscpc.gov.in -telework.gov -tenders.gov.au -textmagic.com -tfhrc.gov -thebell.io -thecoolspot.gov -thinkroadsafety.gov.uk -tiaabank.com -ticketland.ru -tinder.com -tinkoff.ru -tomsk.gov.ru -torbay.gov.uk -tradingstandards.gov.uk -treas.gov -trial-sport.ru -tsa.gov -tst.gov.br both:1.0 -tuba.gov.tr -turystyka.gov.pl -tutu.ru -twitch.tv -twitter.com -twix.com -uber.com -ucarecdn.com -ucrdatatool.gov -udall.gov -ukvisas.gov.uk -ulmart.ru -unicor.gov -uniras.gov.uk -ups.com -uralairlines.ru -us-cert.gov -usa.gov -usadf.gov -usaid.gov -usap.gov -uscg.gov -usconsulate.gov -usmission.gov -usphs.gov -uspis.gov -usps.com -usps.gov -ustreas.gov -utair.ru -utkonos.ru -vaccines.gov -valeofglamorgan.gov.uk -verizonwireless.com -vigoda.ru -visa.co.uk -visa.com -visa.com.ar -visa.com.br both:1.0 -visa.com.cn -visa.com.tw -visa.pl -vistacampus.gov -vk.com bl:1.0 -vkrugudruzei.ru -vkusnyblog.ru -vmc.gov.in -voa.gov.uk -volunteer.gov -vote.gov -walsall.gov.uk -wandsworth.gov.uk -wartimecontracting.gov -warwickdc.gov.uk -wealden.gov.uk -wellingtonfl.gov -west-lindsey.gov.uk -westernunion.com -westernunion.ru -westlothian.gov.uk -whatsapp.com -whistleblowers.gov -wirral.gov.uk -wlga.gov.uk -womenshealth.gov -wrexham.gov.uk -wrigley.com -wrp.gov -yandex-team.ru -york.gov.uk -youla.ru -youth.gov -youthrules.gov -youtube.com -zcts.ru -zendesk.com -zionsbank.com -zomato.com diff --git a/resources/config/lists/free.txt.gz b/resources/config/lists/free.txt.gz deleted file mode 100644 index 79b9ffef..00000000 Binary files a/resources/config/lists/free.txt.gz and /dev/null differ diff --git a/resources/config/lists/mid.inc b/resources/config/lists/mid.inc deleted file mode 100644 index d3ec5ce8..00000000 --- a/resources/config/lists/mid.inc +++ /dev/null @@ -1,42 +0,0 @@ -# Known invalid or missed Message-IDs -# 'domain' 'Message-ID regexp' - -1c-bitrix.ru -alibaba.com /^[a-f0-9]{8}(?:-[a-f0-9]{4}){3}-[a-f0-9]{12}-0$/ -aliexpress.com /^(?:(?:[0-9]{14,16}|[a-z]{4}(?:COWS|UTT)_[0-9]{3,6}_(?:(?:[0-9]{7,8}|0)_)?)[$])?[a-f0-9]{32}$/ -beeline.ru /^<[A-z0-9+]{18}>$/ -cdek.ru -citilink.ru -noreply.esphere.ru -etpgpb.ru -noreply.etprf.ru -freshports.org -rkn.gov.ru -garant.ru -is-zakupki.com -kassir.ru -mail-us.jira.com /^[a-f0-9]{8}(?:-[a-f0-9]{4}){3}-[a-f0-9]{12}$/ -mirtesen.ru -fcod.nalog.ru -www.nalog.ru -otc.ru -news.ozon.ru /^[A-z0-9+/]{32}$/ -sender.ozon.ru /^[A-z0-9+/]{32}$/ -professionali.ru -psbank.ru -qiwi.ru -robokassa.ru -client.rostelecom.ru -sberbank-ast.ru -taxcom.ru -crm.taxcom.ru -wildberries.ru /^[a-f0-9]{8}(?:-[a-f0-9]{4}){3}-[a-f0-9]{12}$/ -promo.wildberries.ru /^[A-F0-9]{8}(?:-[A-F0-9]{4}){3}-[A-F0-9]{12}$/ -taxi.yandex.ru /^[a-f0-9]{32}$/ -mail.youdo.com -online.zcts.ru -mail.authorize.net -craigslist.org -maersk.com -monster.com -teamviewer.com diff --git a/resources/config/lists/mime_types.inc b/resources/config/lists/mime_types.inc deleted file mode 100644 index 9821faa7..00000000 --- a/resources/config/lists/mime_types.inc +++ /dev/null @@ -1,1546 +0,0 @@ -# perl -ne 'if(/([^\d.\s\[o][^\d.\[\]\s\/A-Z]{3,}\/[^\s\/]+)/) { print "$1 0\n"; }' -application/1d-interleaved-parityfec 0 -application/3gpdash-qoe-report+xml 0 -application/3gpp-ims+xml 0 -application/A2L 0 -application/AML 0 -application/ATF 0 -application/ATFX 0 -application/ATXML 0 -application/CALS-1840 0 -application/CDFX+XML 0 -application/CEA 0 -application/CSTAdata+xml 0 -application/DCD 0 -application/DII 0 -application/DIT 0 -application/EDI-X12 0 -application/EDI-consent 0 -application/EDIFACT 0 -application/EmergencyCallData.Comment+xml 0 -application/EmergencyCallData.DeviceInfo+xml 0 -application/EmergencyCallData.ProviderInfo+xml 0 -application/EmergencyCallData.ServiceInfo+xml 0 -application/EmergencyCallData.SubscriberInfo+xml 0 -application/H224 0 -application/IOTP 0 -application/ISUP 0 -application/LXF 0 -application/MF4 0 -application/ODA 0 -application/ODX 0 -application/PDX 0 -application/QSIG 0 -application/SGML 0 -application/activemessage 0 -application/alto-costmap+json 0 -application/alto-costmapfilter+json 0 -application/alto-directory+json 0 -application/alto-endpointcost+json 0 -application/alto-endpointcostparams+json 0 -application/alto-endpointprop+json 0 -application/alto-endpointpropparams+json 0 -application/alto-error+json 0 -application/alto-networkmap+json 0 -application/alto-networkmapfilter+json 0 -application/andrew-inset 0 -application/applefile 0 -application/atom+xml 0 -application/atomcat+xml 0 -application/atomdeleted+xml 0 -application/atomicmail 0 -application/atomsvc+xml 0 -application/auth-policy+xml 0 -application/bacnet-xdd+zip 0 -application/batch-SMTP 0 -application/beep+xml 0 -application/calendar+json 0 -application/calendar+xml 0 -application/call-completion 0 -application/cbor 0 -application/ccmp+xml 0 -application/ccxml+xml 0 -application/cdmi-capability 0 -application/cdmi-container 0 -application/cdmi-domain 0 -application/cdmi-object 0 -application/cdmi-queue 0 -application/cdni 0 -application/cea-2018+xml 0 -application/cellml+xml 0 -application/cfw 0 -application/cms 0 -application/cnrp+xml 0 -application/coap-group+json 0 -application/commonground 0 -application/conference-info+xml 0 -application/cpl+xml 0 -application/csrattrs 0 -application/csta+xml 0 -application/csvm+json 0 -application/cybercash 0 -application/dash+xml 0 -application/dashdelta 0 -application/davmount+xml 0 -application/dca-rft 0 -application/dec-dx 0 -application/dialog-info+xml 0 -application/dicom 0 -application/dns 0 -application/dskpp+xml 0 -application/dssc+der 0 -application/dssc+xml 0 -application/dvcs 0 -application/ecmascript 0 -application/emotionml+xml 0 -application/encaprtp 0 -application/epp+xml 0 -application/epub+zip 0 -application/eshop 0 -application/example 0 -application/fastinfoset 0 -application/fastsoap 0 -application/fdt+xml 0 -application/fits 0 -application/font-sfnt 0 -application/font-tdpfr 0 -application/font-woff 0 -application/framework-attributes+xml 0 -application/gzip 0 -application/held+xml 0 -application/http 0 -application/hyperstudio 0 -application/ibe-key-request+xml 0 -application/ibe-pkg-reply+xml 0 -application/ibe-pp-data 0 -application/ics 0 -application/iges 0 -application/im-iscomposing+xml 0 -application/index 0 -application/index-obj 0 -application/index.cmd 0 -application/index.response 0 -application/index.vnd 0 -application/inkml+xml 0 -application/ipfix 0 -application/ipp 0 -application/its+xml 0 -application/javascript 0 -application/jose 0 -application/jose+json 0 -application/jrd+json 0 -application/json 0 -application/json-patch+json 0 -application/json-seq 0 -application/jwk+json 0 -application/jwk-set+json 0 -application/jwt 0 -application/kpml-request+xml 0 -application/kpml-response+xml 0 -application/ld+json 0 -application/link-format 0 -application/load-control+xml 0 -application/lost+xml 0 -application/lostsync+xml 0 -application/mac-binhex40 0 -application/macwriteii 0 -application/mads+xml 0 -application/marc 0 -application/marcxml+xml 0 -application/mathematica 0 -application/mbms-associated-procedure-description+xml 0 -application/mbms-deregister+xml 0 -application/mbms-envelope+xml 0 -application/mbms-msk+xml 0 -application/mbms-msk-response+xml 0 -application/mbms-protection-description+xml 0 -application/mbms-reception-report+xml 0 -application/mbms-register+xml 0 -application/mbms-register-response+xml 0 -application/mbms-schedule+xml 0 -application/mbms-user-service-description+xml 0 -application/mbox 0 -application/media-policy-dataset+xml 0 -application/media_control+xml 0 -application/mediaservercontrol+xml 0 -application/merge-patch+json 0 -application/metalink4+xml 0 -application/mets+xml 0 -application/mikey 0 -application/mods+xml 0 -application/moss-keys 0 -application/moss-signature 0 -application/mosskey-data 0 -application/mosskey-request 0 -application/mp21 0 -application/mp4 0 -application/mpeg4-generic 0 -application/mpeg4-iod 0 -application/mpeg4-iod-xmt 0 -application/mrb-consumer+xml 0 -application/mrb-publish+xml 0 -application/msc-ivr+xml 0 -application/msc-mixer+xml 0 -application/msword 0 -application/mxf 0 -application/nasdata 0 -application/news-checkgroups 0 -application/news-groupinfo 0 -application/news-transmission 0 -application/nlsml+xml 0 -application/nss 0 -application/ocsp-request 0 -application/ocsp-response 0 -application/octet-stream 0 -application/oebps-package+xml 0 -application/ogg 0 -application/oxps 0 -application/p2p-overlay+xml 0 -application/patch-ops-error+xml 0 -application/pdf 0 -application/pgp-encrypted 0 -application/pgp-signature 0 -application/pidf+xml 0 -application/pidf-diff+xml 0 -application/pkcs10 0 -application/pkcs12 0 -application/pkcs7-mime 0 -application/pkcs7-signature 0 -application/pkcs8 0 -application/pkix-attr-cert 0 -application/pkix-cert 0 -application/pkix-crl 0 -application/pkix-pkipath 0 -application/pkixcmp 0 -application/pls+xml 0 -application/poc-settings+xml 0 -application/postscript 0 -application/ppsp-tracker+json 0 -application/provenance+xml 0 -application/prs.alvestrand.titrax-sheet 0 -application/prs.cww 0 -application/prs.hpub+zip 0 -application/prs.nprend 0 -application/prs.plucker 0 -application/prs.rdf-xml-crypt 0 -application/prs.xsf+xml 0 -application/pskc+xml 0 -application/rar 0 -application/raptorfec 0 -application/rdap+json 0 -application/rdf+xml 0 -application/reginfo+xml 0 -application/relax-ng-compact-syntax 0 -application/remote-printing 0 -application/reputon+json 0 -application/resource-lists+xml 0 -application/resource-lists-diff+xml 0 -application/rfc+xml 0 -application/riscos 0 -application/rlmi+xml 0 -application/rls-services+xml 0 -application/rpki-ghostbusters 0 -application/rpki-manifest 0 -application/rpki-roa 0 -application/rpki-updown 0 -application/rtf 0 -application/rtploopback 0 -application/rtx 0 -application/samlassertion+xml 0 -application/samlmetadata+xml 0 -application/sbml+xml 0 -application/scaip+xml 0 -application/scim+json 0 -application/scvp-cv-request 0 -application/scvp-cv-response 0 -application/scvp-vp-request 0 -application/scvp-vp-response 0 -application/sdp 0 -application/sep+xml 0 -application/sep-exi 0 -application/session-info 0 -application/set-payment 0 -application/set-payment-initiation 0 -application/set-registration 0 -application/set-registration-initiation 0 -application/sgml-open-catalog 0 -application/shf+xml 0 -application/sieve 0 -application/simple-filter+xml 0 -application/simple-message-summary 0 -application/simpleSymbolContainer 0 -application/slate 0 -application/smil+xml 0 -application/smpte336m 0 -application/soap+fastinfoset 0 -application/soap+xml 0 -application/spirits-event+xml 0 -application/sql 0 -application/srgs 0 -application/srgs+xml 0 -application/sru+xml 0 -application/ssml+xml 0 -application/tamp-apex-update 0 -application/tamp-apex-update-confirm 0 -application/tamp-community-update 0 -application/tamp-community-update-confirm 0 -application/tamp-error 0 -application/tamp-sequence-adjust 0 -application/tamp-sequence-adjust-confirm 0 -application/tamp-status-query 0 -application/tamp-status-response 0 -application/tamp-update 0 -application/tamp-update-confirm 0 -application/tei+xml 0 -application/thraud+xml 0 -application/timestamp-query 0 -application/timestamp-reply 0 -application/timestamped-data 0 -application/ttml+xml 0 -application/tve-trigger 0 -application/ulpfec 0 -application/urc-grpsheet+xml 0 -application/urc-ressheet+xml 0 -application/urc-targetdesc+xml 0 -application/urc-uisocketdesc+xml 0 -application/vcard+json 0 -application/vcard+xml 0 -application/vemmi 0 -application/vnd-acucobol 0 -application/vnd-curl 0 -application/vnd-dart 0 -application/vnd-dxr 0 -application/vnd-fdf 0 -application/vnd-mif 0 -application/vnd-sema 0 -application/vnd-wap-wmlc 0 -application/vnd.3M.Post-it-Notes 0 -application/vnd.3gpp-prose+xml 0 -application/vnd.3gpp-prose-pc3ch+xml 0 -application/vnd.3gpp.SRVCC-info+xml 0 -application/vnd.3gpp.access-transfer-events+xml 0 -application/vnd.3gpp.bsf+xml 0 -application/vnd.3gpp.mid-call+xml 0 -application/vnd.3gpp.pic-bw-large 0 -application/vnd.3gpp.pic-bw-small 0 -application/vnd.3gpp.pic-bw-var 0 -application/vnd.3gpp.sms 0 -application/vnd.3gpp.srvcc-ext+xml 0 -application/vnd.3gpp.state-and-event-info+xml 0 -application/vnd.3gpp.ussd+xml 0 -application/vnd.3gpp2.bcmcsinfo+xml 0 -application/vnd.3gpp2.sms 0 -application/vnd.3gpp2.tcap 0 -application/vnd.FloGraphIt 0 -application/vnd.HandHeld-Entertainment+xml 0 -application/vnd.Kinar 0 -application/vnd.MFER 0 -application/vnd.Mobius.DAF 0 -application/vnd.Mobius.DIS 0 -application/vnd.Mobius.MBK 0 -application/vnd.Mobius.MQY 0 -application/vnd.Mobius.MSL 0 -application/vnd.Mobius.PLC 0 -application/vnd.Mobius.TXF 0 -application/vnd.Quark.QuarkXPress 0 -application/vnd.SimTech-MindMapper 0 -application/vnd.accpac.simply.aso 0 -application/vnd.accpac.simply.imp 0 -application/vnd.acucorp 0 -application/vnd.adobe.flash-movie 0 -application/vnd.adobe.formscentral.fcdt 0 -application/vnd.adobe.fxp 0 -application/vnd.adobe.partial-upload 0 -application/vnd.adobe.xdp+xml 0 -application/vnd.adobe.xfdf 0 -application/vnd.aether.imp 0 -application/vnd.ah-barcode 0 -application/vnd.ahead.space 0 -application/vnd.airzip.filesecure.azf 0 -application/vnd.airzip.filesecure.azs 0 -application/vnd.americandynamics.acc 0 -application/vnd.amiga.ami 0 -application/vnd.amundsen.maze+xml 0 -application/vnd.anki 0 -application/vnd.anser-web-certificate-issue-initiation 0 -application/vnd.antix.game-component 0 -application/vnd.apache.thrift.binary 0 -application/vnd.apache.thrift.compact 0 -application/vnd.apache.thrift.json 0 -application/vnd.api+json 0 -application/vnd.apple.installer+xml 0 -application/vnd.apple.mpegurl 0 -application/vnd.arastra.swi 0 -application/vnd.aristanetworks.swi 0 -application/vnd.artsquare 0 -application/vnd.astraea-software.iota 0 -application/vnd.audiograph 0 -application/vnd.autopackage 0 -application/vnd.avistar+xml 0 -application/vnd.balsamiq.bmml+xml 0 -application/vnd.balsamiq.bmpr 0 -application/vnd.bekitzur-stech+json 0 -application/vnd.biopax.rdf+xml 0 -application/vnd.blueice.multipass 0 -application/vnd.bluetooth.ep.oob 0 -application/vnd.bluetooth.le.oob 0 -application/vnd.bmi 0 -application/vnd.businessobjects 0 -application/vnd.cab-jscript 0 -application/vnd.canon-cpdl 0 -application/vnd.canon-lips 0 -application/vnd.cendio.thinlinc.clientconf 0 -application/vnd.century-systems.tcp_stream 0 -application/vnd.chemdraw+xml 0 -application/vnd.chipnuts.karaoke-mmd 0 -application/vnd.cinderella 0 -application/vnd.cirpack.isdn-ext 0 -application/vnd.citationstyles.style+xml 0 -application/vnd.claymore 0 -application/vnd.cloanto.rp9 0 -application/vnd.clonk.c4group 0 -application/vnd.cluetrust.cartomobile-config 0 -application/vnd.cluetrust.cartomobile-config-pkg 0 -application/vnd.coffeescript 0 -application/vnd.collection+json 0 -application/vnd.collection.doc+json 0 -application/vnd.collection.next+json 0 -application/vnd.commerce-battelle 0 -application/vnd.commonspace 0 -application/vnd.contact.cmsg 0 -application/vnd.cosmocaller 0 -application/vnd.crick.clicker 0 -application/vnd.crick.clicker.keyboard 0 -application/vnd.crick.clicker.palette 0 -application/vnd.crick.clicker.template 0 -application/vnd.crick.clicker.wordbank 0 -application/vnd.criticaltools.wbs+xml 0 -application/vnd.ctc-posml 0 -application/vnd.ctct.ws+xml 0 -application/vnd.cups-pdf 0 -application/vnd.cups-postscript 0 -application/vnd.cups-ppd 0 -application/vnd.cups-raster 0 -application/vnd.cups-raw 0 -application/vnd.cyan.dean.root+xml 0 -application/vnd.cybank 0 -application/vnd.data-vision.rdz 0 -application/vnd.debian.binary-package 0 -application/vnd.dece-zip 0 -application/vnd.dece.data 0 -application/vnd.dece.ttml+xml 0 -application/vnd.dece.unspecified 0 -application/vnd.denovo.fcselayout-link 0 -application/vnd.desmume-movie 0 -application/vnd.dir-bi.plate-dl-nosuffix 0 -application/vnd.dm.delegation+xml 0 -application/vnd.dna 0 -application/vnd.document+json 0 -application/vnd.dolby.mobile.1 0 -application/vnd.dolby.mobile.2 0 -application/vnd.doremir.scorecloud-binary-document 0 -application/vnd.dpgraph 0 -application/vnd.dreamfactory 0 -application/vnd.drive+json 0 -application/vnd.dtg.local 0 -application/vnd.dtg.local-html 0 -application/vnd.dtg.local.flash 0 -application/vnd.dvb.ait 0 -application/vnd.dvb.dvbj 0 -application/vnd.dvb.esgcontainer 0 -application/vnd.dvb.ipdcdftnotifaccess 0 -application/vnd.dvb.ipdcesgaccess 0 -application/vnd.dvb.ipdcesgaccess2 0 -application/vnd.dvb.ipdcesgpdd 0 -application/vnd.dvb.ipdcroaming 0 -application/vnd.dvb.iptv.alfec-base 0 -application/vnd.dvb.iptv.alfec-enhancement 0 -application/vnd.dvb.notif-aggregate-root+xml 0 -application/vnd.dvb.notif-container+xml 0 -application/vnd.dvb.notif-generic+xml 0 -application/vnd.dvb.notif-ia-msglist+xml 0 -application/vnd.dvb.notif-ia-registration-request+xml 0 -application/vnd.dvb.notif-ia-registration-response+xml 0 -application/vnd.dvb.notif-init+xml 0 -application/vnd.dvb.pfr 0 -application/vnd.dvb_service 0 -application/vnd.dynageo 0 -application/vnd.dzr 0 -application/vnd.easykaraoke.cdgdownload 0 -application/vnd.ecdis-update 0 -application/vnd.ecowin.chart 0 -application/vnd.ecowin.filerequest 0 -application/vnd.ecowin.fileupdate 0 -application/vnd.ecowin.series 0 -application/vnd.ecowin.seriesrequest 0 -application/vnd.ecowin.seriesupdate 0 -application/vnd.emclient.accessrequest+xml 0 -application/vnd.enliven 0 -application/vnd.enphase.envoy 0 -application/vnd.eprints.data+xml 0 -application/vnd.epson.esf 0 -application/vnd.epson.msf 0 -application/vnd.epson.quickanime 0 -application/vnd.epson.salt 0 -application/vnd.epson.ssf 0 -application/vnd.ericsson.quickcall 0 -application/vnd.eszigno3+xml 0 -application/vnd.etsi.aoc+xml 0 -application/vnd.etsi.asic-e+zip 0 -application/vnd.etsi.asic-s+zip 0 -application/vnd.etsi.cug+xml 0 -application/vnd.etsi.iptvcommand+xml 0 -application/vnd.etsi.iptvdiscovery+xml 0 -application/vnd.etsi.iptvprofile+xml 0 -application/vnd.etsi.iptvsad-bc+xml 0 -application/vnd.etsi.iptvsad-cod+xml 0 -application/vnd.etsi.iptvsad-npvr+xml 0 -application/vnd.etsi.iptvservice+xml 0 -application/vnd.etsi.iptvsync+xml 0 -application/vnd.etsi.iptvueprofile+xml 0 -application/vnd.etsi.mcid+xml 0 -application/vnd.etsi.mheg5 0 -application/vnd.etsi.overload-control-policy-dataset+xml 0 -application/vnd.etsi.pstn+xml 0 -application/vnd.etsi.sci+xml 0 -application/vnd.etsi.simservs+xml 0 -application/vnd.etsi.timestamp-token 0 -application/vnd.etsi.tsl+xml 0 -application/vnd.etsi.tsl.der 0 -application/vnd.eudora.data 0 -application/vnd.ezpix-album 0 -application/vnd.ezpix-package 0 -application/vnd.f-secure.mobile 0 -application/vnd.fastcopy-disk-image 0 -application/vnd.fdsn.mseed 0 -application/vnd.fdsn.seed 0 -application/vnd.ffsns 0 -application/vnd.filmit.zfc 0 -application/vnd.fints 0 -application/vnd.firemonkeys.cloudcell 0 -application/vnd.fluxtime.clip 0 -application/vnd.font-fontforge-sfd 0 -application/vnd.framemaker 0 -application/vnd.frogans.fnc 0 -application/vnd.frogans.ltf 0 -application/vnd.fsc.weblaunch 0 -application/vnd.fujitsu.oasys 0 -application/vnd.fujitsu.oasys2 0 -application/vnd.fujitsu.oasys3 0 -application/vnd.fujitsu.oasysgp 0 -application/vnd.fujitsu.oasysprs 0 -application/vnd.fujixerox.ART-EX 0 -application/vnd.fujixerox.ART4 0 -application/vnd.fujixerox.HBPL 0 -application/vnd.fujixerox.ddd 0 -application/vnd.fujixerox.docuworks 0 -application/vnd.fujixerox.docuworks.binder 0 -application/vnd.fujixerox.docuworks.container 0 -application/vnd.fut-misnet 0 -application/vnd.fuzzysheet 0 -application/vnd.genomatix.tuxedo 0 -application/vnd.geo+json 0 -application/vnd.geocube+xml 0 -application/vnd.geogebra.file 0 -application/vnd.geogebra.tool 0 -application/vnd.geometry-explorer 0 -application/vnd.geonext 0 -application/vnd.geoplan 0 -application/vnd.geospace 0 -application/vnd.gerber 0 -application/vnd.globalplatform.card-content-mgt 0 -application/vnd.globalplatform.card-content-mgt-response 0 -application/vnd.gmx 0 -application/vnd.google-earth.kml+xml 0 -application/vnd.google-earth.kmz 0 -application/vnd.gov.sk.e-form+xml 0 -application/vnd.gov.sk.e-form+zip 0 -application/vnd.gov.sk.xmldatacontainer+xml 0 -application/vnd.grafeq 0 -application/vnd.gridmp 0 -application/vnd.groove-account 0 -application/vnd.groove-help 0 -application/vnd.groove-identity-message 0 -application/vnd.groove-injector 0 -application/vnd.groove-tool-message 0 -application/vnd.groove-tool-template 0 -application/vnd.groove-vcard 0 -application/vnd.hal+json 0 -application/vnd.hal+xml 0 -application/vnd.hbci 0 -application/vnd.hcl-bireports 0 -application/vnd.hdt 0 -application/vnd.heroku+json 0 -application/vnd.hhe.lesson-player 0 -application/vnd.hp-HPGL 0 -application/vnd.hp-PCL 0 -application/vnd.hp-PCLXL 0 -application/vnd.hp-hpid 0 -application/vnd.hp-hps 0 -application/vnd.hp-jlyt 0 -application/vnd.httphone 0 -application/vnd.hydrostatix.sof-data 0 -application/vnd.hyperdrive+json 0 -application/vnd.hzn-3d-crossword 0 -application/vnd.ibm.MiniPay 0 -application/vnd.ibm.afplinedata 0 -application/vnd.ibm.electronic-media 0 -application/vnd.ibm.modcap 0 -application/vnd.ibm.rights-management 0 -application/vnd.ibm.secure-container 0 -application/vnd.iccprofile 0 -application/vnd.ieee.1905 0 -application/vnd.igloader 0 -application/vnd.immervision-ivp 0 -application/vnd.immervision-ivu 0 -application/vnd.ims.imsccv1p1 0 -application/vnd.ims.imsccv1p2 0 -application/vnd.ims.imsccv1p3 0 -application/vnd.ims.lis.v2.result+json 0 -application/vnd.ims.lti.v2.toolconsumerprofile+json 0 -application/vnd.ims.lti.v2.toolproxy+json 0 -application/vnd.ims.lti.v2.toolproxy.id+json 0 -application/vnd.ims.lti.v2.toolsettings+json 0 -application/vnd.ims.lti.v2.toolsettings.simple+json 0 -application/vnd.informedcontrol.rms+xml 0 -application/vnd.infotech.project 0 -application/vnd.infotech.project+xml 0 -application/vnd.innopath.wamp.notification 0 -application/vnd.insors.igm 0 -application/vnd.intercon.formnet 0 -application/vnd.intergeo 0 -application/vnd.intertrust.digibox 0 -application/vnd.intertrust.nncp 0 -application/vnd.intu.qbo 0 -application/vnd.intu.qfx 0 -application/vnd.iptc.g2.catalogitem+xml 0 -application/vnd.iptc.g2.conceptitem+xml 0 -application/vnd.iptc.g2.knowledgeitem+xml 0 -application/vnd.iptc.g2.newsitem+xml 0 -application/vnd.iptc.g2.newsmessage+xml 0 -application/vnd.iptc.g2.packageitem+xml 0 -application/vnd.iptc.g2.planningitem+xml 0 -application/vnd.ipunplugged.rcprofile 0 -application/vnd.irepository.package+xml 0 -application/vnd.is-xpr 0 -application/vnd.isac.fcs 0 -application/vnd.jam 0 -application/vnd.japannet-directory-service 0 -application/vnd.japannet-jpnstore-wakeup 0 -application/vnd.japannet-payment-wakeup 0 -application/vnd.japannet-registration 0 -application/vnd.japannet-registration-wakeup 0 -application/vnd.japannet-setstore-wakeup 0 -application/vnd.japannet-verification 0 -application/vnd.japannet-verification-wakeup 0 -application/vnd.jcp.javame.midlet-rms 0 -application/vnd.jisp 0 -application/vnd.joost.joda-archive 0 -application/vnd.jsk.isdn-ngn 0 -application/vnd.kahootz 0 -application/vnd.kde.karbon 0 -application/vnd.kde.kchart 0 -application/vnd.kde.kformula 0 -application/vnd.kde.kivio 0 -application/vnd.kde.kontour 0 -application/vnd.kde.kpresenter 0 -application/vnd.kde.kspread 0 -application/vnd.kde.kword 0 -application/vnd.kenameaapp 0 -application/vnd.kidspiration 0 -application/vnd.koan 0 -application/vnd.kodak-descriptor 0 -application/vnd.las.las+xml 0 -application/vnd.liberty-request+xml 0 -application/vnd.llamagraphics.life-balance.desktop 0 -application/vnd.llamagraphics.life-balance.exchange+xml 0 -application/vnd.lotus-1-2-3 0 -application/vnd.lotus-approach 0 -application/vnd.lotus-freelance 0 -application/vnd.lotus-notes 0 -application/vnd.lotus-organizer 0 -application/vnd.lotus-screencam 0 -application/vnd.lotus-wordpro 0 -application/vnd.macports.portpkg 0 -application/vnd.mapbox-vector-tile 0 -application/vnd.marlin.drm.actiontoken+xml 0 -application/vnd.marlin.drm.conftoken+xml 0 -application/vnd.marlin.drm.license+xml 0 -application/vnd.marlin.drm.mdcf 0 -application/vnd.mason+json 0 -application/vnd.maxmind.maxmind-db 0 -application/vnd.mcd 0 -application/vnd.medcalcdata 0 -application/vnd.mediastation.cdkey 0 -application/vnd.meridian-slingshot 0 -application/vnd.mfmp 0 -application/vnd.micro+json 0 -application/vnd.micrografx-igx 0 -application/vnd.micrografx.flo 0 -application/vnd.microsoft.portable-executable 0 -application/vnd.miele+json 0 -application/vnd.minisoft-hp3000-save 0 -application/vnd.mitsubishi.misty-guard.trustweb 0 -application/vnd.mophun.application 0 -application/vnd.mophun.certificate 0 -application/vnd.motorola.flexsuite 0 -application/vnd.motorola.flexsuite.adsi 0 -application/vnd.motorola.flexsuite.fis 0 -application/vnd.motorola.flexsuite.gotap 0 -application/vnd.motorola.flexsuite.kmr 0 -application/vnd.motorola.flexsuite.ttc 0 -application/vnd.motorola.flexsuite.wem 0 -application/vnd.motorola.iprm 0 -application/vnd.mozilla.xul+xml 0 -application/vnd.ms-3mfdocument 0 -application/vnd.ms-PrintDeviceCapabilities+xml 0 -application/vnd.ms-artgalry 0 -application/vnd.ms-asf 0 -application/vnd.ms-cab-compressed 0 -application/vnd.ms-excel 0 -application/vnd.ms-excel.addin.macroEnabled.12 0 -application/vnd.ms-excel.sheet.binary.macroEnabled.12 0 -application/vnd.ms-excel.sheet.macroEnabled.12 0 -application/vnd.ms-excel.template.macroEnabled.12 0 -application/vnd.ms-fontobject 0 -application/vnd.ms-htmlhelp 0 -application/vnd.ms-ims 0 -application/vnd.ms-lrm 0 -application/vnd.ms-office.activeX+xml 0 -application/vnd.ms-officetheme 0 -application/vnd.ms-playready.initiator+xml 0 -application/vnd.ms-powerpoint 0 -application/vnd.ms-powerpoint.addin.macroEnabled.12 0 -application/vnd.ms-powerpoint.presentation.macroEnabled.12 0 -application/vnd.ms-powerpoint.slide.macroEnabled.12 0 -application/vnd.ms-powerpoint.slideshow.macroEnabled.12 0 -application/vnd.ms-powerpoint.template.macroEnabled.12 0 -application/vnd.ms-project 0 -application/vnd.ms-tnef 0 -application/vnd.ms-windows.devicepairing 0 -application/vnd.ms-windows.nwprinting.oob 0 -application/vnd.ms-windows.printerpairing 0 -application/vnd.ms-windows.wsd.oob 0 -application/vnd.ms-wmdrm.lic-chlg-req 0 -application/vnd.ms-wmdrm.lic-resp 0 -application/vnd.ms-wmdrm.meter-chlg-req 0 -application/vnd.ms-wmdrm.meter-resp 0 -application/vnd.ms-word.document.macroEnabled.12 0 -application/vnd.ms-word.template.macroEnabled.12 0 -application/vnd.ms-works 0 -application/vnd.ms-wpl 0 -application/vnd.ms-xpsdocument 0 -application/vnd.msa-disk-image 0 -application/vnd.mseq 0 -application/vnd.msign 0 -application/vnd.multiad.creator 0 -application/vnd.multiad.creator.cif 0 -application/vnd.music-niff 0 -application/vnd.musician 0 -application/vnd.muvee.style 0 -application/vnd.mynfc 0 -application/vnd.ncd.control 0 -application/vnd.ncd.reference 0 -application/vnd.nervana 0 -application/vnd.netfpx 0 -application/vnd.neurolanguage.nlu 0 -application/vnd.nintendo.nitro.rom 0 -application/vnd.nintendo.snes.rom 0 -application/vnd.nitf 0 -application/vnd.noblenet-directory 0 -application/vnd.noblenet-sealer 0 -application/vnd.noblenet-web 0 -application/vnd.nokia.catalogs 0 -application/vnd.nokia.conml+wbxml 0 -application/vnd.nokia.conml+xml 0 -application/vnd.nokia.iSDS-radio-presets 0 -application/vnd.nokia.iptv.config+xml 0 -application/vnd.nokia.landmark+wbxml 0 -application/vnd.nokia.landmark+xml 0 -application/vnd.nokia.landmarkcollection+xml 0 -application/vnd.nokia.n-gage.ac+xml 0 -application/vnd.nokia.n-gage.data 0 -application/vnd.nokia.n-gage.symbian.install 0 -application/vnd.nokia.ncd 0 -application/vnd.nokia.pcd+wbxml 0 -application/vnd.nokia.pcd+xml 0 -application/vnd.nokia.radio-preset 0 -application/vnd.nokia.radio-presets 0 -application/vnd.novadigm.EDM 0 -application/vnd.novadigm.EDX 0 -application/vnd.novadigm.EXT 0 -application/vnd.ntt-local.content-share 0 -application/vnd.ntt-local.file-transfer 0 -application/vnd.ntt-local.ogw_remote-access 0 -application/vnd.ntt-local.sip-ta_remote 0 -application/vnd.ntt-local.sip-ta_tcp_stream 0 -application/vnd.oasis.opendocument.chart 0 -application/vnd.oasis.opendocument.chart-template 0 -application/vnd.oasis.opendocument.database 0 -application/vnd.oasis.opendocument.formula 0 -application/vnd.oasis.opendocument.formula-template 0 -application/vnd.oasis.opendocument.graphics 0 -application/vnd.oasis.opendocument.graphics-template 0 -application/vnd.oasis.opendocument.image 0 -application/vnd.oasis.opendocument.image-template 0 -application/vnd.oasis.opendocument.presentation 0 -application/vnd.oasis.opendocument.presentation-template 0 -application/vnd.oasis.opendocument.spreadsheet 0 -application/vnd.oasis.opendocument.spreadsheet-template 0 -application/vnd.oasis.opendocument.text 0 -application/vnd.oasis.opendocument.text-master 0 -application/vnd.oasis.opendocument.text-template 0 -application/vnd.oasis.opendocument.text-web 0 -application/vnd.obn 0 -application/vnd.oftn.l10n+json 0 -application/vnd.oipf.contentaccessdownload+xml 0 -application/vnd.oipf.contentaccessstreaming+xml 0 -application/vnd.oipf.cspg-hexbinary 0 -application/vnd.oipf.dae.svg+xml 0 -application/vnd.oipf.dae.xhtml+xml 0 -application/vnd.oipf.mippvcontrolmessage+xml 0 -application/vnd.oipf.pae.gem 0 -application/vnd.oipf.spdiscovery+xml 0 -application/vnd.oipf.spdlist+xml 0 -application/vnd.oipf.ueprofile+xml 0 -application/vnd.oipf.userprofile+xml 0 -application/vnd.olpc-sugar 0 -application/vnd.oma-scws-config 0 -application/vnd.oma-scws-http-request 0 -application/vnd.oma-scws-http-response 0 -application/vnd.oma.bcast.associated-procedure-parameter+xml 0 -application/vnd.oma.bcast.drm-trigger+xml 0 -application/vnd.oma.bcast.imd+xml 0 -application/vnd.oma.bcast.ltkm 0 -application/vnd.oma.bcast.notification+xml 0 -application/vnd.oma.bcast.provisioningtrigger 0 -application/vnd.oma.bcast.sgboot 0 -application/vnd.oma.bcast.sgdd+xml 0 -application/vnd.oma.bcast.sgdu 0 -application/vnd.oma.bcast.simple-symbol-container 0 -application/vnd.oma.bcast.smartcard-trigger+xml 0 -application/vnd.oma.bcast.sprov+xml 0 -application/vnd.oma.bcast.stkm 0 -application/vnd.oma.cab-address-book+xml 0 -application/vnd.oma.cab-feature-handler+xml 0 -application/vnd.oma.cab-pcc+xml 0 -application/vnd.oma.cab-subs-invite+xml 0 -application/vnd.oma.cab-user-prefs+xml 0 -application/vnd.oma.dcd 0 -application/vnd.oma.dcdc 0 -application/vnd.oma.dd2+xml 0 -application/vnd.oma.drm.risd+xml 0 -application/vnd.oma.group-usage-list+xml 0 -application/vnd.oma.pal+xml 0 -application/vnd.oma.poc.detailed-progress-report+xml 0 -application/vnd.oma.poc.final-report+xml 0 -application/vnd.oma.poc.groups+xml 0 -application/vnd.oma.poc.invocation-descriptor+xml 0 -application/vnd.oma.poc.optimized-progress-report+xml 0 -application/vnd.oma.push 0 -application/vnd.oma.scidm.messages+xml 0 -application/vnd.oma.xcap-directory+xml 0 -application/vnd.omads-email+xml 0 -application/vnd.omads-file+xml 0 -application/vnd.omads-folder+xml 0 -application/vnd.omaloc-supl-init 0 -application/vnd.openblox.game+xml 0 -application/vnd.openblox.game-binary 0 -application/vnd.openeye.oeb 0 -application/vnd.openxmlformats-officedocument.custom-properties+xml 0 -application/vnd.openxmlformats-officedocument.customXmlProperties+xml 0 -application/vnd.openxmlformats-officedocument.drawing+xml 0 -application/vnd.openxmlformats-officedocument.drawingml.chart+xml 0 -application/vnd.openxmlformats-officedocument.drawingml.chartshapes+xml 0 -application/vnd.openxmlformats-officedocument.drawingml.diagramColors+xml 0 -application/vnd.openxmlformats-officedocument.drawingml.diagramData+xml 0 -application/vnd.openxmlformats-officedocument.drawingml.diagramLayout+xml 0 -application/vnd.openxmlformats-officedocument.drawingml.diagramStyle+xml 0 -application/vnd.openxmlformats-officedocument.extended-properties+xml 0 -application/vnd.openxmlformats-officedocument.presentationml-template 0 -application/vnd.openxmlformats-officedocument.presentationml.commentAuthors+xml 0 -application/vnd.openxmlformats-officedocument.presentationml.comments+xml 0 -application/vnd.openxmlformats-officedocument.presentationml.handoutMaster+xml 0 -application/vnd.openxmlformats-officedocument.presentationml.notesMaster+xml 0 -application/vnd.openxmlformats-officedocument.presentationml.notesSlide+xml 0 -application/vnd.openxmlformats-officedocument.presentationml.presProps+xml 0 -application/vnd.openxmlformats-officedocument.presentationml.presentation 0 -application/vnd.openxmlformats-officedocument.presentationml.presentation.main+xml 0 -application/vnd.openxmlformats-officedocument.presentationml.slide 0 -application/vnd.openxmlformats-officedocument.presentationml.slide+xml 0 -application/vnd.openxmlformats-officedocument.presentationml.slideLayout+xml 0 -application/vnd.openxmlformats-officedocument.presentationml.slideMaster+xml 0 -application/vnd.openxmlformats-officedocument.presentationml.slideUpdateInfo+xml 0 -application/vnd.openxmlformats-officedocument.presentationml.slideshow 0 -application/vnd.openxmlformats-officedocument.presentationml.slideshow.main+xml 0 -application/vnd.openxmlformats-officedocument.presentationml.tableStyles+xml 0 -application/vnd.openxmlformats-officedocument.presentationml.tags+xml 0 -application/vnd.openxmlformats-officedocument.presentationml.template.main+xml 0 -application/vnd.openxmlformats-officedocument.presentationml.viewProps+xml 0 -application/vnd.openxmlformats-officedocument.spreadsheetml-template 0 -application/vnd.openxmlformats-officedocument.spreadsheetml.calcChain+xml 0 -application/vnd.openxmlformats-officedocument.spreadsheetml.chartsheet+xml 0 -application/vnd.openxmlformats-officedocument.spreadsheetml.comments+xml 0 -application/vnd.openxmlformats-officedocument.spreadsheetml.connections+xml 0 -application/vnd.openxmlformats-officedocument.spreadsheetml.dialogsheet+xml 0 -application/vnd.openxmlformats-officedocument.spreadsheetml.externalLink+xml 0 -application/vnd.openxmlformats-officedocument.spreadsheetml.pivotCacheDefinition+xml 0 -application/vnd.openxmlformats-officedocument.spreadsheetml.pivotCacheRecords+xml 0 -application/vnd.openxmlformats-officedocument.spreadsheetml.pivotTable+xml 0 -application/vnd.openxmlformats-officedocument.spreadsheetml.queryTable+xml 0 -application/vnd.openxmlformats-officedocument.spreadsheetml.revisionHeaders+xml 0 -application/vnd.openxmlformats-officedocument.spreadsheetml.revisionLog+xml 0 -application/vnd.openxmlformats-officedocument.spreadsheetml.sharedStrings+xml 0 -application/vnd.openxmlformats-officedocument.spreadsheetml.sheet 0 -application/vnd.openxmlformats-officedocument.spreadsheetml.sheet.main+xml 0 -application/vnd.openxmlformats-officedocument.spreadsheetml.sheetMetadata+xml 0 -application/vnd.openxmlformats-officedocument.spreadsheetml.styles+xml 0 -application/vnd.openxmlformats-officedocument.spreadsheetml.table+xml 0 -application/vnd.openxmlformats-officedocument.spreadsheetml.tableSingleCells+xml 0 -application/vnd.openxmlformats-officedocument.spreadsheetml.template.main+xml 0 -application/vnd.openxmlformats-officedocument.spreadsheetml.userNames+xml 0 -application/vnd.openxmlformats-officedocument.spreadsheetml.volatileDependencies+xml 0 -application/vnd.openxmlformats-officedocument.spreadsheetml.worksheet+xml 0 -application/vnd.openxmlformats-officedocument.theme+xml 0 -application/vnd.openxmlformats-officedocument.themeOverride+xml 0 -application/vnd.openxmlformats-officedocument.vmlDrawing 0 -application/vnd.openxmlformats-officedocument.wordprocessingml-template 0 -application/vnd.openxmlformats-officedocument.wordprocessingml.comments+xml 0 -application/vnd.openxmlformats-officedocument.wordprocessingml.document 0 -application/vnd.openxmlformats-officedocument.wordprocessingml.document.glossary+xml 0 -application/vnd.openxmlformats-officedocument.wordprocessingml.document.main+xml 0 -application/vnd.openxmlformats-officedocument.wordprocessingml.endnotes+xml 0 -application/vnd.openxmlformats-officedocument.wordprocessingml.fontTable+xml 0 -application/vnd.openxmlformats-officedocument.wordprocessingml.footer+xml 0 -application/vnd.openxmlformats-officedocument.wordprocessingml.footnotes+xml 0 -application/vnd.openxmlformats-officedocument.wordprocessingml.numbering+xml 0 -application/vnd.openxmlformats-officedocument.wordprocessingml.settings+xml 0 -application/vnd.openxmlformats-officedocument.wordprocessingml.styles+xml 0 -application/vnd.openxmlformats-officedocument.wordprocessingml.template.main+xml 0 -application/vnd.openxmlformats-officedocument.wordprocessingml.webSettings+xml 0 -application/vnd.openxmlformats-package.core-properties+xml 0 -application/vnd.openxmlformats-package.digital-signature-xmlsignature+xml 0 -application/vnd.openxmlformats-package.relationships+xml 0 -application/vnd.oracle.resource+json 0 -application/vnd.orange.indata 0 -application/vnd.osa.netdeploy 0 -application/vnd.osgeo.mapguide.package 0 -application/vnd.osgi.bundle 0 -application/vnd.osgi.dp 0 -application/vnd.osgi.subsystem 0 -application/vnd.otps.ct-kip+xml 0 -application/vnd.oxli.countgraph 0 -application/vnd.pagerduty+json 0 -application/vnd.palm 0 -application/vnd.panoply 0 -application/vnd.paos+xml 0 -application/vnd.pawaafile 0 -application/vnd.pcos 0 -application/vnd.pg.format 0 -application/vnd.pg.osasli 0 -application/vnd.piaccess.application-licence 0 -application/vnd.picsel 0 -application/vnd.pmi.widget 0 -application/vnd.poc.group-advertisement+xml 0 -application/vnd.pocketlearn 0 -application/vnd.powerbuilder6 0 -application/vnd.powerbuilder6-s 0 -application/vnd.powerbuilder7 0 -application/vnd.powerbuilder7-s 0 -application/vnd.powerbuilder75 0 -application/vnd.powerbuilder75-s 0 -application/vnd.preminet 0 -application/vnd.previewsystems.box 0 -application/vnd.proteus.magazine 0 -application/vnd.publishare-delta-tree 0 -application/vnd.pvi.ptid1 0 -application/vnd.pwg-multiplexed 0 -application/vnd.pwg-xhtml-print+xml 0 -application/vnd.qualcomm.brew-app-res 0 -application/vnd.quobject-quoxdocument 0 -application/vnd.radisys.moml+xml 0 -application/vnd.radisys.msml+xml 0 -application/vnd.radisys.msml-audit+xml 0 -application/vnd.radisys.msml-audit-conf+xml 0 -application/vnd.radisys.msml-audit-conn+xml 0 -application/vnd.radisys.msml-audit-dialog+xml 0 -application/vnd.radisys.msml-audit-stream+xml 0 -application/vnd.radisys.msml-conf+xml 0 -application/vnd.radisys.msml-dialog+xml 0 -application/vnd.radisys.msml-dialog-base+xml 0 -application/vnd.radisys.msml-dialog-fax-detect+xml 0 -application/vnd.radisys.msml-dialog-fax-sendrecv+xml 0 -application/vnd.radisys.msml-dialog-group+xml 0 -application/vnd.radisys.msml-dialog-speech+xml 0 -application/vnd.radisys.msml-dialog-transform+xml 0 -application/vnd.rainstor.data 0 -application/vnd.rapid 0 -application/vnd.realvnc.bed 0 -application/vnd.recordare.musicxml 0 -application/vnd.recordare.musicxml+xml 0 -application/vnd.renlearn.rlprint 0 -application/vnd.rig.cryptonote 0 -application/vnd.route66.link66+xml 0 -application/vnd.rs-274x 0 -application/vnd.ruckus.download 0 -application/vnd.s3sms 0 -application/vnd.sailingtracker.track 0 -application/vnd.sbm.cid 0 -application/vnd.sbm.mid2 0 -application/vnd.scribus 0 -application/vnd.sealed-doc 0 -application/vnd.sealed-eml 0 -application/vnd.sealed-mht 0 -application/vnd.sealed-ppt 0 -application/vnd.sealed-tiff 0 -application/vnd.sealed-xls 0 -application/vnd.sealed.3df 0 -application/vnd.sealed.csf 0 -application/vnd.sealed.net 0 -application/vnd.sealedmedia.softseal-html 0 -application/vnd.sealedmedia.softseal-pdf 0 -application/vnd.seemail 0 -application/vnd.semd 0 -application/vnd.semf 0 -application/vnd.shana.informed.formdata 0 -application/vnd.shana.informed.formtemplate 0 -application/vnd.shana.informed.interchange 0 -application/vnd.shana.informed.package 0 -application/vnd.siren+json 0 -application/vnd.smaf 0 -application/vnd.smart.notebook 0 -application/vnd.smart.teacher 0 -application/vnd.software602.filler.form+xml 0 -application/vnd.software602.filler.form-xml-zip 0 -application/vnd.solent.sdkm+xml 0 -application/vnd.spotfire.dxp 0 -application/vnd.spotfire.sfs 0 -application/vnd.sss-cod 0 -application/vnd.sss-dtf 0 -application/vnd.sss-ntf 0 -application/vnd.stepmania.package 0 -application/vnd.stepmania.stepchart 0 -application/vnd.street-stream 0 -application/vnd.sun.wadl+xml 0 -application/vnd.sus-calendar 0 -application/vnd.svd 0 -application/vnd.swiftview-ics 0 -application/vnd.syncml+xml 0 -application/vnd.syncml.dm+wbxml 0 -application/vnd.syncml.dm+xml 0 -application/vnd.syncml.dm.notification 0 -application/vnd.syncml.dmddf+wbxml 0 -application/vnd.syncml.dmddf+xml 0 -application/vnd.syncml.dmtnds+wbxml 0 -application/vnd.syncml.dmtnds+xml 0 -application/vnd.syncml.ds.notification 0 -application/vnd.tao.intent-module-archive 0 -application/vnd.tcpdump.pcap 0 -application/vnd.tmd.mediaflex.api+xml 0 -application/vnd.tml 0 -application/vnd.tmobile-livetv 0 -application/vnd.trid.tpt 0 -application/vnd.triscape.mxs 0 -application/vnd.trueapp 0 -application/vnd.truedoc 0 -application/vnd.ubisoft.webplayer 0 -application/vnd.ufdl 0 -application/vnd.uiq.theme 0 -application/vnd.umajin 0 -application/vnd.unity 0 -application/vnd.uoml+xml 0 -application/vnd.uplanet.alert 0 -application/vnd.uplanet.alert-wbxml 0 -application/vnd.uplanet.bearer-choice 0 -application/vnd.uplanet.bearer-choice-wbxml 0 -application/vnd.uplanet.cacheop 0 -application/vnd.uplanet.cacheop-wbxml 0 -application/vnd.uplanet.channel 0 -application/vnd.uplanet.channel-wbxml 0 -application/vnd.uplanet.list 0 -application/vnd.uplanet.list-wbxml 0 -application/vnd.uplanet.listcmd 0 -application/vnd.uplanet.listcmd-wbxml 0 -application/vnd.uplanet.signal 0 -application/vnd.uri-map 0 -application/vnd.valve.source.material 0 -application/vnd.vcx 0 -application/vnd.vd-study 0 -application/vnd.vectorworks 0 -application/vnd.verimatrix.vcas 0 -application/vnd.vidsoft.vidconference 0 -application/vnd.visio 0 -application/vnd.visionary 0 -application/vnd.vividence.scriptfile 0 -application/vnd.vsf 0 -application/vnd.wap-slc 0 -application/vnd.wap-wbxml 0 -application/vnd.wap.sic 0 -application/vnd.wap.wmlscriptc 0 -application/vnd.webturbo 0 -application/vnd.wfa.p2p 0 -application/vnd.wfa.wsc 0 -application/vnd.windows.devicepairing 0 -application/vnd.wmc 0 -application/vnd.wmf.bootstrap 0 -application/vnd.wolfram.mathematica 0 -application/vnd.wolfram.mathematica.package 0 -application/vnd.wolfram.player 0 -application/vnd.wordperfect 0 -application/vnd.wqd 0 -application/vnd.wrq-hp3000-labelled 0 -application/vnd.wt.stf 0 -application/vnd.wv.csp+wbxml 0 -application/vnd.wv.csp+xml 0 -application/vnd.wv.ssp+xml 0 -application/vnd.xacml+json 0 -application/vnd.xara 0 -application/vnd.xfdl 0 -application/vnd.xfdl.webform 0 -application/vnd.xmi+xml 0 -application/vnd.xmpie.cpkg 0 -application/vnd.xmpie.dpkg 0 -application/vnd.xmpie.plan 0 -application/vnd.xmpie.ppkg 0 -application/vnd.xmpie.xlim 0 -application/vnd.yamaha.hv-dic 0 -application/vnd.yamaha.hv-script 0 -application/vnd.yamaha.hv-voice 0 -application/vnd.yamaha.openscoreformat 0 -application/vnd.yamaha.openscoreformat.osfpvg+xml 0 -application/vnd.yamaha.remote-setup 0 -application/vnd.yamaha.smaf-audio 0 -application/vnd.yamaha.smaf-phrase 0 -application/vnd.yamaha.through-ngn 0 -application/vnd.yamaha.tunnel-udpencap 0 -application/vnd.yaoweme 0 -application/vnd.yellowriver-custom-menu 0 -application/vnd.zul 0 -application/vnd.zzazz.deck+xml 0 -application/voicexml+xml 0 -application/vq-rtcpxr 0 -application/watcherinfo+xml 0 -application/whoispp-query 0 -application/whoispp-response 0 -application/wita 0 -application/wordperfect5.1 0 -application/wsdl+xml 0 -application/wspolicy+xml 0 -application/x-excel 0 -application/x-gzip 0 -application/x-msdownload 0 -application/x-pkcs12 0 -application/x-www-form-urlencoded 0 -application/x-tar 0 -application/x-x509-ca-cert 0 -application/x-7z-compressed 0 -application/x-zip-compressed 0 -application/x400-bp 0 -application/xacml+xml 0 -application/xcap-att+xml 0 -application/xcap-caps+xml 0 -application/xcap-diff+xml 0 -application/xcap-el+xml 0 -application/xcap-error+xml 0 -application/xcap-ns+xml 0 -application/xcon-conference-info+xml 0 -application/xcon-conference-info-diff+xml 0 -application/xenc+xml 0 -application/xhtml+xml 0 -application/xml 0 -application/xml-dtd 0 -application/xml-external-parsed-entity 0 -application/xml-patch+xml 0 -application/xmpp+xml 0 -application/xop+xml 0 -application/xv+xml 0 -application/yang 0 -application/yin+xml 0 -application/zip 0 -application/zlib 0 -audio/1d-interleaved-parityfec 0 -audio/32kadpcm 0 -audio/3gpp 0 -audio/3gpp2 0 -audio/AMR 0 -audio/AMR-WB 0 -audio/ATRAC-ADVANCED-LOSSLESS 0 -audio/ATRAC-X 0 -audio/ATRAC3 0 -audio/BV16 0 -audio/BV32 0 -audio/CN 0 -audio/DAT12 0 -audio/DV 0 -audio/DVI4 0 -audio/EVRC 0 -audio/EVRC-QCP 0 -audio/EVRC0 0 -audio/EVRC1 0 -audio/EVRCB 0 -audio/EVRCB0 0 -audio/EVRCB1 0 -audio/EVRCNW 0 -audio/EVRCNW0 0 -audio/EVRCNW1 0 -audio/EVRCWB 0 -audio/EVRCWB0 0 -audio/EVRCWB1 0 -audio/EVS 0 -audio/G711-0 0 -audio/G719 0 -audio/G722 0 -audio/G7221 0 -audio/G723 0 -audio/G726-16 0 -audio/G726-24 0 -audio/G726-32 0 -audio/G726-40 0 -audio/G728 0 -audio/G729 0 -audio/G729D 0 -audio/G729E 0 -audio/GSM 0 -audio/GSM-EFR 0 -audio/GSM-HR-08 0 -audio/L16 0 -audio/L20 0 -audio/L24 0 -audio/L8 0 -audio/LPC 0 -audio/MP4A-LATM 0 -audio/MPA 0 -audio/PCMA 0 -audio/PCMA-WB 0 -audio/PCMU 0 -audio/PCMU-WB 0 -audio/RED 0 -audio/SMV 0 -audio/SMV-QCP 0 -audio/SMV0 0 -audio/UEMCLIP 0 -audio/VDVI 0 -audio/VMR-WB 0 -audio/ac3 0 -audio/amr-wb+ 0 -audio/aptx 0 -audio/asc 0 -audio/basic 0 -audio/clearmode 0 -audio/dls 0 -audio/dsr-es201108 0 -audio/dsr-es202050 0 -audio/dsr-es202211 0 -audio/dsr-es202212 0 -audio/eac3 0 -audio/encaprtp 0 -audio/example 0 -audio/fwdred 0 -audio/iLBC 0 -audio/ip-mr_v2.5 0 -audio/mobile-xmf 0 -audio/mp4 0 -audio/mpa-robust 0 -audio/mpeg 0 -audio/mpeg3 0 -audio/mpeg4-generic 0 -audio/ogg 0 -audio/opus 0 -audio/prs.sid 0 -audio/qcelp 0 -audio/raptorfec 0 -audio/rtp-enc-aescm128 0 -audio/rtp-midi 0 -audio/rtploopback 0 -audio/rtx 0 -audio/sp-midi 0 -audio/speex 0 -audio/t140c 0 -audio/t38 0 -audio/telephone-event 0 -audio/tone 0 -audio/ulpfec 0 -audio/vnd.3gpp.iufp 0 -audio/vnd.4SB 0 -audio/vnd.CELP 0 -audio/vnd.audiokoz 0 -audio/vnd.cisco.nse 0 -audio/vnd.cmles.radio-events 0 -audio/vnd.cns.anp1 0 -audio/vnd.cns.inf1 0 -audio/vnd.dece.audio 0 -audio/vnd.digital-winds 0 -audio/vnd.dlna.adts 0 -audio/vnd.dolby.heaac.1 0 -audio/vnd.dolby.heaac.2 0 -audio/vnd.dolby.mlp 0 -audio/vnd.dolby.mps 0 -audio/vnd.dolby.pl2 0 -audio/vnd.dolby.pl2x 0 -audio/vnd.dolby.pl2z 0 -audio/vnd.dolby.pulse.1 0 -audio/vnd.dra 0 -audio/vnd.dts 0 -audio/vnd.dts.hd 0 -audio/vnd.dvb.file 0 -audio/vnd.everad.plj 0 -audio/vnd.hns.audio 0 -audio/vnd.lucent.voice 0 -audio/vnd.ms-playready.media.pya 0 -audio/vnd.nokia.mobile-xmf 0 -audio/vnd.nortel.vbk 0 -audio/vnd.nuera.ecelp4800 0 -audio/vnd.nuera.ecelp7470 0 -audio/vnd.nuera.ecelp9600 0 -audio/vnd.octel.sbc 0 -audio/vnd.rhetorex.32kadpcm 0 -audio/vnd.rip 0 -audio/vnd.sealedmedia.softseal-mpeg 0 -audio/vnd.vmx.cvsd 0 -audio/vnd.wave 0 -audio/vorbis 0 -audio/vorbis-config 0 -audio/wav 0 -audio/x-mpeg-3 0 -audio/x-wav 0 -image/bmp 0 -image/cgm 0 -image/example 0 -image/fits 0 -image/g3fax 0 -image/gif 0 -image/jp2 0 -image/jpeg 0 -image/jpm 0 -image/jpx 0 -image/naplps 0 -image/png 0 -image/prs.btif 0 -image/prs.pti 0 -image/pwg-raster 0 -image/t38 0 -image/tiff 0 -image/tiff-fx 0 -image/vnd-djvu 0 -image/vnd-svf 0 -image/vnd-wap-wbmp 0 -image/vnd.adobe.photoshop 0 -image/vnd.airzip.accelerator.azv 0 -image/vnd.cns.inf2 0 -image/vnd.dece.graphic 0 -image/vnd.dvb.subtitle 0 -image/vnd.dwg 0 -image/vnd.dxf 0 -image/vnd.fastbidsheet 0 -image/vnd.fpx 0 -image/vnd.fst 0 -image/vnd.fujixerox.edmics-mmr 0 -image/vnd.fujixerox.edmics-rlc 0 -image/vnd.globalgraphics.pgb 0 -image/vnd.microsoft.icon 0 -image/vnd.mix 0 -image/vnd.mozilla.apng 0 -image/vnd.ms-modi 0 -image/vnd.net-fpx 0 -image/vnd.radiance 0 -image/vnd.sealed-png 0 -image/vnd.sealedmedia.softseal-gif 0 -image/vnd.sealedmedia.softseal-jpg 0 -image/vnd.tencent.tap 0 -image/vnd.valve.source.texture 0 -image/vnd.xiff 0 -image/vnd.zbrush.pcx 0 -image/wmf 0 -image/x-wmf 0 -message/CPIM 0 -message/delivery-status 0 -message/disposition-notification 0 -message/example 0 -message/feedback-report 0 -message/global 0 -message/global-delivery-status 0 -message/global-disposition-notification 0 -message/global-headers 0 -message/http 0 -message/imdn+xml 0 -message/news 0 -message/rfc822 0 -message/rfc822-headers 0 -message/s-http 0 -message/sip 0 -message/sipfrag 0 -message/tracking-status 0 -message/vnd.si.simp 0 -message/vnd.wfa.wsc 0 -model/example 0 -model/iges 0 -model/vnd-dwf 0 -model/vnd.collada+xml 0 -model/vnd.flatland.3dml 0 -model/vnd.gdl 0 -model/vnd.gs-gdl 0 -model/vnd.gtw 0 -model/vnd.moml+xml 0 -model/vnd.mts 0 -model/vnd.opengex 0 -model/vnd.parasolid.transmit-binary 0 -model/vnd.parasolid.transmit-text 0 -model/vnd.valve.source.compiled-map 0 -model/vnd.vtu 0 -model/x3d+fastinfoset 0 -model/x3d+xml 0 -model/x3d-vrml 0 -multipart/alternative -1 -multipart/mixed -1 -multipart/appledouble 2 -multipart/byteranges 2 -multipart/encrypted -2 -multipart/example 2 -multipart/form-data 2 -multipart/header-set 2 -multipart/related -1 -multipart/report 0 -multipart/signed -2 -multipart/voice-message 2 -multipart/x-mixed-replace 2 -text/1d-interleaved-parityfec 0 -text/RED 0 -text/SGML 0 -text/cache-manifest 0 -text/calendar 0 -text/css 0 -text/csv 0 -text/csv-schema 0 -text/directory 0 -text/dns 0 -text/encaprtp 0 -text/example 0 -text/fwdred 0 -text/grammar-ref-list 0 -text/html 0 -text/jcr-cnd 0 -text/markdown 0 -text/mizar 0 -text/n3 0 -text/parameters 0 -text/provenance-notation 0 -text/prs.fallenstein.rst 0 -text/prs.lines.tag 0 -text/plain -1 -text/raptorfec 0 -text/rfc822-headers 0 -text/rtf 0 -text/rtp-enc-aescm128 0 -text/rtploopback 0 -text/rtx 0 -text/t140 0 -text/tab-separated-values 0 -text/troff 0 -text/turtle 0 -text/ulpfec 0 -text/uri-list 0 -text/vcard 0 -text/vnd-a 0 -text/vnd-curl 0 -text/vnd.DMClientScript 0 -text/vnd.IPTC.NITF 0 -text/vnd.IPTC.NewsML 0 -text/vnd.abc 0 -text/vnd.debian.copyright 0 -text/vnd.dvb.subtitle 0 -text/vnd.esmertec.theme-descriptor 0 -text/vnd.fly 0 -text/vnd.fmi.flexstor 0 -text/vnd.graphviz 0 -text/vnd.in3d.3dml 0 -text/vnd.in3d.spot 0 -text/vnd.latex-z 0 -text/vnd.motorola.reflex 0 -text/vnd.ms-mediapackage 0 -text/vnd.net2phone.commcenter.command 0 -text/vnd.radisys.msml-basic-layout 0 -text/vnd.si.uricatalogue 0 -text/vnd.sun.j2me.app-descriptor 0 -text/vnd.trolltech.linguist 0 -text/vnd.wap-wml 0 -text/vnd.wap.si 0 -text/vnd.wap.sl 0 -text/vnd.wap.wmlscript 0 -text/xml 0 -text/xml-external-parsed-entity 0 -text/x-vcard 0 -video/1d-interleaved-parityfec 0 -video/3gpp 0 -video/3gpp-tt 0 -video/3gpp2 0 -video/BMPEG 0 -video/BT656 0 -video/CelB 0 -video/DV 0 -video/H261 0 -video/H263 0 -video/H263-1998 0 -video/H263-2000 0 -video/H264 0 -video/H264-RCDO 0 -video/H264-SVC 0 -video/H265 0 -video/JPEG 0 -video/MP1S 0 -video/MP2P 0 -video/MP2T 0 -video/MP4V-ES 0 -video/MPV 0 -video/SMPTE292M 0 -video/VP8 0 -video/encaprtp 0 -video/example 0 -video/iso.segment 0 -video/jpeg2000 0 -video/mj2 0 -video/mp4 0 -video/mpeg4-generic 0 -video/nv 0 -video/ogg 0 -video/pointer 0 -video/quicktime 0 -video/raptorfec 0 -video/rtp-enc-aescm128 0 -video/rtploopback 0 -video/rtx 0 -video/ulpfec 0 -video/vc1 0 -video/vnd-mpegurl 0 -video/vnd-vivo 0 -video/vnd.CCTV 0 -video/vnd.dece-mp4 0 -video/vnd.dece.hd 0 -video/vnd.dece.mobile 0 -video/vnd.dece.pd 0 -video/vnd.dece.sd 0 -video/vnd.dece.video 0 -video/vnd.directv-mpeg 0 -video/vnd.directv.mpeg-tts 0 -video/vnd.dlna.mpeg-tts 0 -video/vnd.dvb.file 0 -video/vnd.fvt 0 -video/vnd.hns.video 0 -video/vnd.iptvforum.1dparityfec-1010 0 -video/vnd.iptvforum.1dparityfec-2005 0 -video/vnd.iptvforum.2dparityfec-1010 0 -video/vnd.iptvforum.2dparityfec-2005 0 -video/vnd.iptvforum.ttsavc 0 -video/vnd.iptvforum.ttsmpeg2 0 -video/vnd.motorola.video 0 -video/vnd.motorola.videop 0 -video/vnd.ms-playready.media.pyv 0 -video/vnd.nokia.interleaved-multimedia 0 -video/vnd.nokia.videovoip 0 -video/vnd.objectvideo 0 -video/vnd.radgamettools.bink 0 -video/vnd.radgamettools.smacker 0 -video/vnd.sealed-swf 0 -video/vnd.sealed.mpeg1 0 -video/vnd.sealed.mpeg4 0 -video/vnd.sealedmedia.softseal-mov 0 -video/vnd.uvvu-mp4 0 diff --git a/resources/config/lists/phishing_whitelist.inc b/resources/config/lists/phishing_whitelist.inc deleted file mode 100644 index 4679bcdc..00000000 --- a/resources/config/lists/phishing_whitelist.inc +++ /dev/null @@ -1,5 +0,0 @@ -# Used to exclude domains and tlds from phishing checks as they have known legal use - -protect-*.mimecast.com -*.safelinks.protection.outlook.com -urldefense.proofpoint.com diff --git a/resources/config/lists/public_suffix_list.dat.gz b/resources/config/lists/public_suffix_list.dat.gz deleted file mode 100644 index c916d9de..00000000 Binary files a/resources/config/lists/public_suffix_list.dat.gz and /dev/null differ diff --git a/resources/config/lists/redirectors.inc b/resources/config/lists/redirectors.inc deleted file mode 100644 index d3e9d22b..00000000 --- a/resources/config/lists/redirectors.inc +++ /dev/null @@ -1,1076 +0,0 @@ -000d.ru -0845.com -0c.ru -0lv.ru -0pen.me -0rz.tw -10r.us -123url.org -140.uz -17q.com -1c-bitrix.ru -1cl.in -1ink.in -1ink.ru -1iny.com -1lik.net -1link.in -1url.com -1url.in -1-url.net -1-url.ru -2big.at -2dwww.com -2.gp -2it.info -2.ly -2mb.eu -2qu.ru -2sms.ru -2tu.me -2tu.us -2url.org -307.to -3fw.ru -3le.ru -3.ly -3.vu -3x.si -4.gg -4job.ru -4.ly -4ms.me -4p5.com -4ry.ru -4sq.com -4u.gd -4url.cc -4url.tk -5.gp -5link.tk -5pl.us -5url.net -5z8.info -6fr.ru -6.ly -6pn.com -6url.com -6yo.org -70.ru -74job.ru -7.ly -7ly.ru -7pisem.ru -7ruh.com -7ry.us -7xu.org -8.ly -8q.ro -9mp.com -9-n.org -9xi.ru -a1.tc -a2k.in -aa.cx -aafter.us -abe5.com -access.im -action-emails.ru -ad4.us -adf.ly -adjix.com -adsbeta.net -ad.vu -afx.cc -a.gg -ah.ae -aipro.ru -airs.ru -aka-url.com -alic.at -all.fuseurl.com -allshort.ru -all-top.ru -alturl.com -a.md -amzn.to -a.nf -apeurl.com -api.m3653.net -apsense.cc -apu.sh -ar.gy -arm.in -arst.ch -atiny.me -atto.co.za -atu.ca -autodesk.com -avast.com -avoo.net -azc.cc -b23.ru -b2l.me -backupurl.com -bacn.me -bai.lu -bcool.bz -bezurl.com -bi.gl -binged.it -bin.nu -bitby.net -bit.do -bit.gy -bit.ly -bitleyco.cc -bitly.com -bitrix24.ru -biturl.net -bit.uz -bizj.us -bloat.me -bmu.li -boi.re -bq.ro -bravo.ly -briefurl.pl -bsa.ly -bsndsy.ru -budurl.com -bun.ru -bu.tt -byst.ro -byyb.net -bz9.com -campaign-services.directcrm.ru -canurl.com -capello.linkatty.com -capourl.com -care2share.tk -cbs.so -cbuz.com -cctv.ws -cd.vg -cektkp.com -cha.la -chilp.it -chzb.gr -cjb.net -cjt99.tk -clck.ru -cliccami.info -click2.info -clicks.biletix.ru -clicks.citilink.ru -click.email4customers.com -click.emailinfo.mail.hpe.com -click.icptrack.com -click.mlsend.com -click-me.us -clickthru.ca -clickv.tk -cli.gs -clkit.co -cl.lk -cl.ly -clme.ru -cloakreferer.com -clockurl.com -clop.in -cms.im -cmylink.com -cnect.us -comyonet.com -conta.cc -coolestone.com -cort.as -cortas.elpais.com -cot.ag -cowurl.com -cp.bitrix.ru -cr.am -createurl.com -crks.me -crlf.ru -crop.im -crum.pl -ctvr.us -cug.kr -cut4.me -cut.by -cuthut.com -cutt.us -d2u.us -d8z.ru -dai.ly -da.lc -ddp.net -decenturl.com -delivr.com -dev0.ru -dft.ba -digbig.com -di.gd -digg.com -digidns.net -din.gy -directtrafficlink.com -disq.us -dld.bz -dlvr.it -dmanalytics1.com -doiop.com -do.my -dopen.us -dot.tk -dr2.biz -driz.ru -dr.tl -durlz.info -easyuri.com -easyurl.jp -easyurl.net -eepurl.com -ej.uz -elurl.com -email.account.2gis.com -email.mail.ostrovok.ru -email.mxtoolbox.com -email.news.ostrovok.ru -e.mail.ru -em.digium.com -emap.ws -emlstart.com -etdurl.com -eweri.com -exa.im -f1ru.net -fa.by -fanta.linkatty.com -fav.me -fbi.pp.ua -fb.me -fbshare.me -fff.to -ff.im -ffs.cc -fi.gd -fire.to -firsturl.de -firsturl.net -fishurl.ru -flane.info -flavr.be -flic.kr -flq.us -flx.im -fly2.ws -folo.me -fo.my -fon.gs -forex-trade.be -fqav.com -freak.to -freepl.us -free-redirect.tk -freeurl.me -free-url-redirection.com.ru -fur.ly -fuseurl.com -fuzzy.to -fwd4.me -fwds.me -fwib.net -fyad.org -fyn.im -g00.me -gadaf.fi -game-url.com -gentleurl.net -geteml.com -getlink.info -get.sh -get.tf -gho.co -gig140.com -gizmo.do -gl.am -glink.co -gltw.ru -gmetzner.de -gmy.su -gnu.su -go2-url.com -go.9nl.com -go9.us -goandgrab.info -gog.tc -go.it -go-links.net -golook.at -go.ly -good.ly -goo.gl -goo.pm -go.qb.by -goshrink.com -gosite.in -goto.pattayacitythailand.com -gourl.ca -gourl.gr -gourl.it -go-url.ru -go.usa.gov -gri.bz -groteck.com -g.ro.lt -gtgg.us -g.ua -gu.ma -gurl.es -haqm.com -hex.io -hhvx.com -hiderefer.com -hijw.com -hi.kg -hit.kg -hj.to -hlurl.com -hmm.ph -ho.io -hop.clickbank.net -hopclicks.com -ho.pe -hop.kz -href.in -hsblinks.com -htxt.it -hubb.me -huff.to -hulu.com -hurl.me -hurl.ws -huuk.net -hvmnd.org -i2h.de -i5.be -icanhaz.com -idek.net -idelink.com -ifree.kz -ih3.ru -ikeafamilynews.ru -ilix.in -ilnk.me -informer.ru -innogam.es -ino.me -int.kz -ipsha.ru -ir.pe -is.gd -is.gs -issuu.com -itshrunk.com -its.my -iurlz.com -ix.lt -ixr.be -j3w.it -ja.cx -jdem.cz -jewi.sh -jijr.com -jmb.tw -j.mp -jom.la -joo.ru -just.as -juu.cc -keep2.me -kickurl.com -kipq.com -kisaurl.com -ki.tl -kl.am -klck.me -klik.sihitam.com -klx.co -knb.im -kon.tl -kore.us -korta.nu -kqon.com -kr1n.ru -krunchd.com -krz.ch -ktzr.us -l24.cm -l3ss.me -l9k.net -lat.ms -lavvs.com -lcut.us -leeturl.net -leto.tk -liip.to -liltext.com -lin.io -link2me.ru -link.ac -linkbee.com -linkbun.ch -linkcash.biz -linkde.info -linkee.com -link.from.homecredit.ru -link.hhut.ru -linkl.ru -link.mail.1fd-system.ru -link.mail.e-gazeta-unp.ru -link.mail.e.glavbukh-mail.ru -link.mail.fd-online.ru -link.mail.glavbukh-mail.ru -link.mail.unp-client.ru -link.rengo.ru -link.sendsay.ru -linkslash.ca -linkunion.de -linkx.me -linkyy.com -linkzip.net -lip.tc -li.ru -list-manage1.com -list-manage2.com -list-manage.com -little.im -littleurl.net -liurl.cn -livehoster.org -llinks.net -ln0.ru -ln4.me -lnk.by -lnk.cm -lnk.co -lnk.gd -lnk.in -lnk.ly -lnk.ms -lnk.sk -lnkd.in -lnks.gd -lnks.it -lnkstts.com -lnkurl.com -ln-s.net -ln-s.ru -loh.ru -loo.gl -lovebyt.es -low.cc -l.pr -lr.tc -lru.jp -lrwk.com -ltos.ru -lt.tl -lul.es -lurl.no -lx2.net -ly9.net -m4u.in -m7a.org -macte.ch -mail.rambler.ru -mandrillapp.com -mash.to -mee.la -merky.de -metamark.net -micurl.com -migre.me -miliuner.com -miniurl.com -miniurl.net.ru -miniurl.pl -mimecast.com -minu.me -minurl.fr -minyurl.net -minyurl.org -mislead.in -miud.in -mixe.me -mj.is -mjt.lu -mke.me -mlcampaignru.com -mljt.tech -mlsendru.com -mmt.su -mobotix-news.com -mo.by -moby.to -mockurl.com -moourl.com -mp77.com -mrte.ch -mtp.pl -mty.in -mug.gs -murl.kz -mvp.im -mylink4u.info -mylink.to -myloc.me -myooo.info -mypaqe.com -mypl.us -mytinyurl.net -myurl.in -myurl.si -myxx.me -mzan.si -n3n.in -n3r.ru -nbc.co -nblo.gs -nbold.com -ne1.net -netgod.tk -neuf.tk -newhotlink.com -nexturl.ru -nicesharing.com -nik.im -niurl.com -nl.cr -nn.nf -no1.in -no.io -nonameno.com -normalurl.com -notlong.com -not.my -now.am -n.pr -nsfw.in -nutshellurl.com -nxy.in -nyti.ms -oc1.us -oeeq.com -oiurl.com -o.ly -omf.gd -om.ly -omoikane.net -on.cnn.com -on.mktw.net -oogyah.com -oork.com -opurl.us -orbita.co.il -orz.se -ourgplus.at -out.houseofgaga.ru -ovr.me -ow.ly -o-x.fr -p1.fr -pathto.net -paypal-communication.com -pb8.ru -pburl.com -pcw.ro -pduda.mobi -pechkincensor.ru -pechkinspy.ru -peeep.us -peekurl.com -pendek.in -penting.web.id -pfat.de -pho.se -phpm.ru -php-ru.info -pi90.com -picourl.ru -piks.nl -ping.fm -pli.gs -plink.es -plo.cc -ploshadka.ru -plugin.name -plusphp.com -p.ly -pnt.me -pobierz-film.tk -politi.co -ponyurl.com -poo.pr -post.ly -pot.vg -pp.gg -ppt.cc -pra.im -privacy-surf.com -proext.com -professionali.ru -profile.to -proofpoint.com -prourl.de -ptiturl.com -p.tl -pub.vitrue.com -punyurl.com -purl.org -pvh.me -pw.ly -pxlz.org -py6.ru -pygmyurl.com -pysper.com -q32.ru -qick.ws -qid.in -qkr.cc -qlnk.net -qlql.ru -qnh.pl -qr.cx -qr.ee -qrf.in -qr.net -qru.ru -qte.me -qtwk.com -quik.in -qurl.com -qu.tc -qz.bz -rb6.me -r.delphicomponent.ru -rdrct.us -rdr.to -read.bi -readthis.ca -reallytinyurl.com -redir.ec -redirectingat.com -redirects.ca -redirect.subscribe.ru -redire.ru -redirx.com -reduc.in -referer.us -retweet.cc -retwt.me -reurl.org -rhwm.eu -rickroll.it -r.im -ri.ms -riz.gd -rlu.ru -rmse.ru -rnd.ru -romb.su -r-ss.de -rt.nu -rubyurl.com -ru.ly -rurl.org -rurl.ru -rurls.ru -rustech.org -rww.tw -s0bu.ru -s0e.ru -s4c.in -s7y.us -s8.hk -safe.mn -saf.li -safelinks.protection.outlook.com -sami.24.gg -sayabit.com -sbrf.link.info.sberbank.ru -s.coop -scurtare-url.hi2.ro -sdut.us -securityexpert.ru -securl.ru -sendgrid.net -sendit.in -sendsay.ru -sendurl.info -sg4d.com -sg-url.tk -shadyurl.com -share.flocktory.com -shar.es -shink.de -shiturl.com -shli.de -shorl.com -shortb.net -shorten.im -shorten.ws -short.ie -short.im -shortlinks.co.uk -short-me.com -shortner.com -shortn.me -short.nr -short.su -short.to -shorturl.asia -shorturl.com -short-url.co.uk -shorturls.co.uk -shortz.me -shout.to -show.my -shrinkee.com -shrinkify.com -shrinkr.com -shrten.com -shrt.fr -shrtl.com -shrt.st -shrunkin.com -shx.in -simplesite.com -simurl.com -sitefwd.com -sk9.pl -slate.me -sli.su -slki.ru -sl.to -smallr.com -smallurl.in -smallurl.ru -smalur.com -s-m.co -smsh.me -smurl.ca -smurl.name -sn9.ru -sn.im -snipr.com -snipurl.com -snurl.com -softlinemail.ru -sokrati.ru -somexyz.com -song.ly -sorturl.net -so.vg -sp2.ro -spedr.com -speed-tester.info -sq6.ru -srclick.ru -srcom.net -srnk.net -srs.li -srtn.me -starts.com -starturl.com -stat-pulse.com -stnx.at -stump.ws -su.ly -su.pr -surl.co.uk -surl.hu -surl.me -surs.nl -susurl.com -swturl.com -t1ny.net -ta.gd -ta.gg -tagiturl.com -taourl.com -tbd.ly -t.cn -t.co -tcrn.ch -techto.us -tez.co -tgl.net -tgr.me -tgr.ph -th8.us -thexyz.org -thinfi.com -th.ly -thnlnk.com -thurl.in -tie.ly -tighturl.com -tin.cc -tiniuri.com -tiny9.com -tinyarro.ws -tiny.by -tiny.cc -tinyit.cc -tinylink.ca -tinylink.in -tiny.ly -tiny.pl -tiny.ps -tinyuri.ca -tinyurl.com -tinyurl.ru -tki.me -tldr.in -tl.gd -t.lh.com -tlim.ru -tllg.net -tmi.me -tnij.org -tny.com -tny.tc -to8.cc -togoto.us -to.je -to.ly -toma.ai -tos.co -totc.us -tourl.fr -toysr.us -tozm.com -tpm.ly -tra.kz -track-mail.skbkontur.ru -trg.li -trii.us -tr.im -trimurl.me -trk.emlbest.com -trk.klclick.com -trk.klclick1.com -trk.klclick2.com -trk.klclick3.com -trunc.it -trusteml.com -tty.su -tuckinfo.com -tux-pla.net -tvsl.eu -tweet.ms -tweez.me -twhub.com -twirl.at -twitclicks.com -twitter.com -twitterurl.net -twiturl.de -twiu.ru -twurl.cc -twurl.nl -tyny.me -u2s.in -u76.org -ub0.cc -u.info-mg.ru -ukril.ru -ulk.me -ulmart.ru -ulo.me -ulu.lu -u.mavrev.com -umenytt.se -unfake.it -u.nu -updating.me -ur1.ca -ur3.us -ural-tender.ru -url2.ru -url360.me -url3.ru -url4.eu -url4.ru -url4t.com -url5.ru -url66.com -urla.ru -url.az -url.b24.am -urlbit.us -urlborg.com -urlbrief.com -urlcantik.com -urlclick.ru -url.cn -urlcorta.es -url.co.uk -urlcover.com -urlcut.com -urldefender.com -urldefense.com -urldepo.ru -url.dflix.net -urlel.com -urlenco.de -urle.us -url.g4team.com -urlgator.com -urlgeo.me -urlg.in -url-go.com -urlgo.ru -url.ie -urlin.it -urlink.eu -urli.nl -urlite.de -url.lotpatrol.com -urlmint.com -url.mk.ua -urlms.com -urloid.com -urloo.com -urlot.com -urlredo.com -urlscott.com -urls.co.za -url.shinri.biz -urlshorteningservicefortwitter.com -urlshort.me -urls.im -urlsim.com -urlsnip.com -urlsp.in -urlsqueeze.com -urls.vg -urltwit.com -urlu.ms -urlus.ru -url.vsofte.ru -urlx.ie -urlxs.fr -ur.ly -url.yanclex.com -urlz.at -urlzen.com -url-zip.com -urlz.ro -usat.ly -use.my -u.to -uud.in -uuu.su -uww.me -uyurl.com -vani.sh -vash-repetitor.ru -vb.ly -vc8.net -vgn.am -view.my -vk.cc -vl.am -vll.me -vog.me -vovka.com -vst.tv -w3t.org -w55.de -wa.la -wapo.st -wapurl.co.uk -warble.co -webformyself.com -weblist.kharkov.ua -weburl.me -weeclix.com -wez.su -whspr.it -widg.me -wik.ro -wipi.es -wlatcy-moch.tk -wlink.me.uk -wmturls.com -wom.im -wowurl.com -wp.me -wp.nu -wurl.in -wurl.us -wuw.su -ww.tl -clickmeeting.com -grandstreamnetworks.ru -reg.ru -wz.ae -x2t.com -xaa.su -xav.cc -xcqv.com -xcs.me -xd5.net -xdvn.net -xew.co -xops.fr -xp.cm -xr.com -xrl.in -xrls.tk -xrl.us -x-url.com -xurl.es -xurl.jp -x.vu -xvx.su -xw6.com -xxsurl.de -xxw.me -y.ahoo.it -yatuc.com -ydn.ru -ye.pe -yep.it -yfrog.com -yhoo.it -yi.pe -yiyd.com -ysu.me -yuarel.com -yurl.in -y-url.ru -yurl.ru -z0p.de -z2z.ca -zapt.in -zazi.me -zcom.us -zebratelecom.ru -zeep.in -zi.ma -zi.mu -zio.in -zipmyurl.com -zolp.net -zrps.info -zti.me -zud.me -zurl.ws -zxc9.com -zzang.kr -zz.gd diff --git a/resources/config/lists/spf_dkim_whitelist.inc b/resources/config/lists/spf_dkim_whitelist.inc deleted file mode 100644 index 541fc8fa..00000000 --- a/resources/config/lists/spf_dkim_whitelist.inc +++ /dev/null @@ -1,240 +0,0 @@ -# Domains with valid SPF and DKIM - -1cfresh.com -4chan.org -6pm.com -about.com -addthis.com -adf.ly -adobe.com -adp.com -adschemist.com -airbnb.com -airtel.in -alibaba.com -aliexpress.com 2.0 -alipay.com 2.0 -allrecipes.com -amazon.ca -amazon.cn -amazon.co.jp -amazon.com -amazon.co.uk -amazon.de -amazon.es -amazon.fr -amazon.in -amazon.it -amazon.ru -americanexpress.com -ancestry.com -android.com -apple.com -asana.com -att.com -autohome.com.cn -avg.com -aweber.com -badoo.com -bankofamerica.com -basecamp.com -battle.net -bet365.com -biglobe.ne.jp -bitly.com -bleacherreport.com -blogger.com -bloomberg.com -booking.com -box.com -bt.com -capitalone.com -cdiscount.com -change.org -chase.com -cisco.com -citi.com -costco.com -craigslist.org -custhelp.com -dell.com -delta.com -diply.com -discovercard.com -disqus.com -dropbox.com -drweb.com -e-boks.dk -ebay.ca -ebay.com -ebay.com.au -ebay.co.uk -ebay.de -ebay.fr -ebay.in -ebay.it -ebay.ru -etsy.com -evernote.com -expedia.com -facebook.com -fedex.com -fidelity.com -fishki.net -flickr.com -flirchi.com -force.com -freepik.com -gap.com -gawker.com -github.com 2.0 -gizmodo.com -godaddy.com -googleadservices.com -googleusercontent.com -groupon.com -hdfcbank.com -hgtv.com -hh.ru -hm.com -houzz.com -hubspot.com -icicibank.com -icloud.com -ign.com -imgur.com -immobilienscout24.de -indeed.com -indiatimes.com -infusionsoft.com -instagram.com -intel.com -irctc.co.in -kayak.com -kickstarter.com -kijiji.ca -kotaku.com -letsencrypt.org -libero.it -lifehacker.com -likes.com -linkedin.com -linux.com -list-manage.com -mackeeper.com -mailchimp.com -mashable.com -match.com -mercadolibre.com.ar -mercadolivre.com.br -messenger.com -microsoft.com -microsoftonline.com -minmyndighetspost.se -moikrug.ru -mts.ru -neobux.com -netflix.com -newegg.com -nhk.or.jp -nifty.com -nikkeibp.co.jp -nyaa.se -nytimes.com -odnoklassniki.ru -ofd.yandex.ru -ok.ru -olx.ua -overstock.com -ozon.ru -ozon.travel -pandora.com -paypal.ca -paypal.cn -paypal.com -paypal.com -paypal.co.uk -paypal.de -paypal.es -paypal.fr -paypal.it -paypal.ru -paytm.com -pch.com -pinterest.com -porn.com -priceline.com -quora.com -rakuten.co.jp -reddit.com -researchgate.net -salesforce.com -sciencedirect.com -shopify.com -skanetrafiken.se -skat.dk -skatteverket.se -slack.com -slideshare.net -so-net.ne.jp -southwest.com -spotify.com -springer.com -squarespace.com -stalker.com -steampowered.com -stumbleupon.com -surveymonkey.com -swagbucks.com -taboola.com -taleo.net -taobao.com -target.com -taringa.net -taxi.yandex.ru -tele2.ru -thekitchn.com -tochka.com -tokopedia.com -trello.com -tribunnews.com -trulia.com -tumblr.com -twitter.com -ultimate-guitar.com -ups.com -usaa.com -usbank.com -usps.com -verizon.com -verizonwireless.com -vimeo.com -vine.co -vk.com -vmware.com -vtb24.ru -wahoofitness.com -walmart.com -wav.tv -wellsfargo.com -whatsapp.com -wikia.com -wikimedia.org -wikipedia.org -wildberries.ru -wix.com -wordpress.com -wordpress.org -wp.com -xuite.net -xvideos.com -yelp.com -youtube.com -yts.to -zappos.com -zendesk.com -zippyshare.com -zomato.com -zoom.us -zulily.com -zwift.com diff --git a/resources/config/lists/surbl-whitelist.inc b/resources/config/lists/surbl-whitelist.inc deleted file mode 100644 index aa9b45fc..00000000 --- a/resources/config/lists/surbl-whitelist.inc +++ /dev/null @@ -1,831 +0,0 @@ -126.com -163.com -1gost.info -1stnationalbank.com -2o7.net -365online.com -4at1.com -53.com -5iantlavalamp.com -abl.com.pk -about.com -accessbankplc.com -adelphia.net -adib.ae -adobe.com -agora-inc.com -agoramedia.com -aibgb.co.uk -aib.ie -airdriesavingsbank.com -akamai.net -akamaitech.net -aldermore.co.uk -alexa.com -alliancebank.com.my -alliancefg.com -alliantcreditunion.com -alliantcreditunion.org -allianz.de -allybank.com -alterna.ca -amazon.com -americanexpress.ch -americanexpress.com -anadolubank.nl -ancestry.com -anz.com -anz.co.nz -aol.com -apache.org -apple.com -arbuthnotlatham.co.uk -arcamax.com -asb.co.nz -ask.com -astrology.com -atdmt.com -att.net -authorize.net -autorambler.ru -axisbank.co.in -axisbank.com -b2bbank.com -baaderbank.de -baidu.com -baloise.ch -baml.com -banamex.com -bancanetbsc.do -bancanetsantacruz.com.do -bancapulia.it -bancarios.com -bancastato.ch -bancatransilvania.ro -bancobase.com -bancobic.ao -bancobic.pt -bancobpi.pt -banco.bradesco -bancobrasil.com.br -bancochile.cl -bancochile.com -bancoestado.cl -bancofalabella.cl -bancofalabella.com.co -bancofalabella.pe -bancomer.com -bancopopolare.it -bancopostaclick.it -bancoposta.it -bancosantander.es -bancovotorantimcartoes.com.br -bank24.ru -bankalhabib.com -bankaustria.at -bank.barclays.co.uk -bankbgzbnpparibas.pl -bankcardservices.co.uk -bankcomm.com -bankcoop.ch -bankiabancapersonal.es -bankia.com -bankia.es -bankinter.com -bankinter.es -bankmutual.com -bankofamerica.com -bankofcanada.ca -bankofchina.com -bankofcyprus.com -bankofindia.co.nz -bankofireland.com -bank-of-ireland.co.uk -bankofirelanduk.com -bankofoklahoma.com -bankofscotland.co.uk -banksinarmas.com -bankvonroll.ch -bankwest.com.au -banque-casino.fr -banquepopulaire.fr -banquescotia.com -barclaycard.co.uk -barclaycard.de -barclaycard.es -barclays.com -barclays.co.uk -barclayspartnerfinance.com -barclays.sc -barodanzltd.co.nz -basler.ch -bbandt.com -bbc.co.uk -bcentral.com -bci.cl -bcp.com.pe -bcv.ch -bcvs.ch -bekb.ch -bellevue.ch -bellsouth.net -bendigobank.com.au -berliner-bank.de -berliner-sparkasse.de -bfanet.ao -bfi0.com -bgfi.com -bgfionline.com -bgzbnpparibas.pl -billmelater.com -bing.com -bkb.ch -bk.rw -bks.at -blkb.ch -bmocm.com -bmo.com -bmogam.com -bmoharris.com -bmoharrisprivatebankingonline.com -bmoinvestorline.com -bmonesbittburns.com -bnl.it -bnpparibas.com -bnpparibas.fr -boc.cnnz -bonuscard.ch -bpe-gruposantander.com -bpi.pt -bpostbank.be -bradescardonline.com.br -bradesco.com.br -bradescoseguranca.com.br -bridgetrack.com -bridgewaterbank.ca -bsibank.com -btrl.ro -bt-trade.ro -businessonline-boi.com -bzbank.ch -ca-cib.com -ca-egypt.com -cafbank.org -cafe24.com -cafonline.org -caisse-epargne.com -caisse-epargne.fr -caixabank.com -caixa.gov.br -cajasur.es -camsonline.com -canadiandirect.com -capitalone360.com -capitalone.com -capitaloneonline.co.uk -capitecbank.co.za -cariparma.it -carrefour-banque.fr -cartabcc.it -cartabccpos.it -cartasi.it -ca-suisse.com -catalunyacaixa.com -cbg.gm -cbonline.co.uk -cembra.ch -cenbank.org -centralbank.ae -charitybank.org -charter.net -chase.com -chebanca.it -chinatrust.com.tw -cial.ch -cibc.com -cic.ch -cimbclicks.com.my -citibank.ae -citibank.co.in -citibank.com -citibank.co.uk -citibankonline.com -citibusiness.com -citicards.com -citi.com -citi.co.nz -citi.eu -citigroup.com -citizensbank.ca -citizensbank.com -civibank.com -civibank.it -cjb.net -classmates.com -clickbank.net -closebrothers.com -closebrothers.co.uk -clubsc.ch -cnet.com -cnn.com -co.kg -colpatria.com -colpatria.com.co -comcast.net -com.com -commbank.com.au -commerzbank.com -commerzbank.de -com.ne.kr -coopbank.dk -co-operativebank.co.uk -cornerbanca.ch -cornercard.ch -cornercard.com -corner.ch -corporate-ir.net -cosycard.ch -coutts.com -cox.net -craigslist.org -credit-agricole.com -credit-agricole.fr -creditagricole.rs -credit-suisse.com -cs.com -css.ch -ctbcbank.com -ctfs.com -custhelp.com -cwbank.com -cwbankgroup.com -cwt.ca -cybg.com -danskebankas.lt -danskebank.com -danskebank.co.uk -danskebank.de -danskebank.dk -danskebank.ee -danskebank.fi -danskebank.ie -danskebank.no -datatrans.biz -datatrans.ch -daum.net -db.com -dbs.com -dd.se -debian.org -dell.com -demirbank.kg -denizbank.com -desjardins.ca -desjardins.com -deutschebank.be -deutschebank.co.nz -deutsche-bank.de -diamondbank.com -dibpak.com -directnic.com -directtrack.com -discovercard.com -discover.com -discovery.co.za -dnbnord.lt -domain.com -doubleclick.com -dresdner-bank.de -dsbbank.sr -dsbl.org -duncanlawrie.com -earthlink.net -easybank.at -easylnk.com -ebay.com -ebay.co.uk -ebay.de -ebayimg.com -ebaystatic.com -ecobank.com -edgesuite.net -ediets.com -edwardjones.com -egroups.com -e-gulfbank.com -emode.com -esunbank.com.tw -example.com -example.net -example.org -excite.com -facebook.com -fedex.com -fednetbank.com -fidelity.com -fidor.de -finance.com -finansbank.com.tr -finasta.lt -fineco.it -firstbankcard.com -firstmerit.com -firstnational.com -firstnationalmerchantsolutions.com -firsttrustbank.co.uk -flickr.com -fnbc.ca -fnb.co.za -fnb-online.com -freebsd.org -free.fr -friuladria.it -f-secure.com -garantibank.eu -garantibank.nl -garanti.com.tr -gazprombank.ch -gazprombank.ru -generali.es -genevoise.ch -gentoo.org -geocities.com -gkb.ch -gmail.com -gmx.net -go.com -godaddy.com -googleadservices.com -google.co.in -google.com -google.it -google.ru -granitbank.hu -grisoft.com -gtbank.com -halifax.co.uk -hallmark.com -handelsbanken.se -harrodsbank.co.uk -hbl.com -hblibank.com -hblibank.com.pk -hdfcbank.com -heartland.co.nz -hellenicbank.com -hinet.net -hkbea.com -hlb.com.kh -hlb.com.my -hoaresbank.co.uk -home.barclays -hongleongconnect.com.kh -hongleongconnect.com.vn -hongleongconnect.my -hotbar.com -hotmail.com -hotpop.com -hp.com -hsbc.com -hsbc.com.ar -hsbc.com.hk -hsbc.co.nz -hsbc.co.uk -hypovereinsbank.co.uk -hypovereinsbank.de -ibm.com -icbcnz.com -icicibank.co.in -icicibank.com -icicibankprivatebanking.com -icorner.ch -icscards.de -icscards.nl -incredimail.com -ing.be -ing.com -ing-diba.de -ingdirect.ca -ing.lu -ing.nl -ingvysyabank.com -interac.ca -investorplace.com -iobnet.co.in -isbank.com.tr -isbank.de -isbank.ge -isbank.iq -isbankkosova.com -itau.com.br -ivillage.com -joingevalia.com -jpmchase.com -jpmorgan.com -jsafrasarasin.com -julianhodgebank.com -juliusbaer.com -juno.com -jyskebank.dk -kantonalbank.ch -kernel.org -key.com -kiwibank.co.nz -kotak.com -kredytbank.pl -kreissparkasse-schwalm-eder.de -ksklb.de -kutxabank.es -laboralkutxa.com -lacaixa.cat -lacaixa.es -laurentianbank.ca -lbb.de -lcl.com -lcl.fr -li.ru -list.ru -liveinternet.ru -livejournal.com -lloydsbank.com -lloydsbankcommercial.com -lloydsbankinggroup.com -lloydstsb.ch -lloydstsb.co.uk -lombardodier.com -loydsbank.com -lycos.com -m7z.net -mac.com -macromedia.com -maerki-baumann.ch -mail.com -mail.ru -mailscanner.info -mandtbank.com -manulifebank.ca -manulifebankselect.ca -manulife.com -manulifeone.ca -marketwatch.com -mashreqbank.com -mastercard.com -maybank2u.com -maybank2u.com.my -mcafee.com -mchsi.com -mdmbank.com -mechanicsbank.com -medbank.lt -messagelabs.com -metrobankdirect.com -metrobankonline.co.uk -microsoft.com -migbank.com -migrosbank.ch -military.com -mindspring.com -mit.edu -mizuhobank.co.jp -mmwarburg.lu -monster.com -montepio.pt -morganstanley.com -mozilla.com -mps.it -ms.com -msn.com -mufg.jp -myonlineresourcecenter.com -myonlineservices.ch -myspace.com -nate.com -nationalesuisse.ch -nationwide-communications.co.uk -nationwide.co.uk -nationwide-service.co.uk -natwest.com -navyfederal.org -nbc.ca -netflix.com -netscape.com -netscape.net -netzero.net -newyorkfed.org -nibl.com.np -nod32.com -nordea.fi -nordea.lt -nordfynsbank.dk -norisbank.de -norman.com -notenstein.ch -nuvisionfederal.com -nytimes.com -oceanbank.com -onlinesbi.com -openoffice.org -openxmlformats.org -optonline.net -orchardbank.com -osdn.com -ostsaechsische-sparkasse-dresden.de -overstock.com -pacbell.net -pandasoftware.com -passport.com -paylife.at -paypal.be -paypal-brasil.com.br -paypal.ca -paypal.ch -paypal.co.il -paypal.com -paypal.com.au -paypal.com.br -paypal-communication.com -paypal-community.com -paypal.com.mx -paypal.com.pt -paypal.co.uk -paypal-customerfeedback.com -paypal.de -paypal-deutschland.de -paypal.dk -paypal.es -paypal-exchanges.com -paypal.fr -paypal.it -paypal-marketing.co.uk -paypal-marketing.pl -paypal.net -paypal.nl -paypal.no -paypal-notify.com -paypal-now.com -paypalobjects.com -paypal-opwaarderen.nl -paypal-pages.com -paypal.pt -paypal.ru -paypal.se -paypal-search.com -paypal-shopping.co.uk -paypal-techsupport.com -pbebank.com -pcfinancial.ca -peoplepc.com -permanenttsb.ie -plaxo.com -pnc.com -popolarevicenza.it -postbank.de -postepay.it -postfinancearena.ch -postfinance.ch -postfinance.info -price.ru -prodigy.net -publicislamicbank.com.my -rabobank.com -rabobank.co.nz -rabobank.nl -radaruol.com.br -rahnbodmer.ch -raiffeisenbank.rs -raiffeisen.ch -raiffeisen.hu -raiffeisen.li -raiffeisen.ru -rambler-co.ru -rambler.ru -raphaelsbank.com -rbc.com -rbcroyalbank.com -rbs.co.uk -rbssecure.co.uk -rbsworldpay.com -rcb.at rcb.at -real.com -recordbank.be -redhat.com -rediff.com -regiobank.nl -regions.com -regionsnet.com -renasantbank.com -rhbgroup.com -rogersbank.com -rogers.com -rothschildbank.com -rothschild.com -royalbank.com -rr.com -sagepay.com -sagepay.co.uk -sainsburysbank.co.uk -samba.com -santander.cl -santander.com -santander.com.br -santander.com.mx -santandercorretora.com.br -santander.co.uk -santanderesfera.com.br -santandersantiago.cl -sarasin.ch -sbcglobal.net -sberbank.ch -sbs.net.nz -sc.com -schoellerbank.at -scotiabank.ca -scotiabank.com -scotiamocatta.com -scotiaonline.com -s.de -sec.gov -securetrustbank.com -service-sparkasse.de -serviciobancomer.com -sf.net -shawbrook.co.uk -shaw.ca -shkb.ch -shockwave.com -six-group.com -six-payment-services.com -skrill.com -sls-direkt.de -smithbarney.com -snb.ch snb.ch -snsbank.nl -societegenerale.fr -sourceforge.net -spamcop.net -sparda-a.de -sparda-bank-hamburg.de -sparda-b.de -sparda-bw.de -sparda-h.de -sparda-hessen.de -sparda-m.de -sparda-ms.de -sparda-n.de -sparda-ostbayern.de -sparda-sw.de -sparda-verband.de -sparda-west.de -sparkasse.at -sparkasse-bank-malta.com -sparkasse-bielefeld.de -sparkasseblog.de -sparkasse-bochum.de -sparkasse.ch -sparkasse.de -sparkasse-gera-greiz.de -sparkasse-hamm.de -sparkasse-heidelberg.de -sparkasse-ingolstadt.de -sparkasse-mittelthueringen.de -speedera.net -sportsline.com -standardbank.com -standardbank.co.za -standardchartered.com.gh -standardchartered.com.my -subscribe.ru -sun.com -suncorpbank.com.au -suntrust.com -swedbank.com -swedbank.ee -swedbank.lt -swedbank.lu -swedbank.se -swisscanto.ch -swisscaution.ch -swissquote.ch -sydbank.dk -sympatico.ca -tails.nl -tangerine.ca -tcb-bank.com.tw -tdbank.com -tdcommercialbanking.com -telus.net -terra.com.br -tescobank.com -ticketmaster.com -tinyurl.com -tiscali.co.uk -tns-counter.ru -tom.com -tone.co.nz -t-online.de -top4top.ru -tsbbank.co.nz -tsb.co.nz -tsb.co.uk -tux.org -twitter.com -ubibanca.com -ubs.com -ulsterbankanytimebanking.co.uk -ulsterbank.co.uk -unibancoconnect.pt -unibanco.pt -unicreditbank.lt -unicredit.eu -unicreditgroup.eu -unicredit.it -unionbankcameroon.com -unionbank.com -unity.co.uk -uob.com.sg -uobgroup.com -uol.com.br -ups.com -usbank.com -valianttrust.com -vaudoise.ch -venetobanca.it -venetobanka.al -verizon.net -versabank.com -videobank.it -virginmoney.com -visa.com.ar -visa.com.br -visaeurope.ch -visaeurope.com -viseca.ch -volksbank.de -volkswagenbank.de -vpbank.com -vr.de -vwbank.de -w3.org -wachovia.com -walmart.com -wamu.com -wanadoo.fr -washingtonpost.com -weatherbug.com -weatherbys.co.uk -web.de -webshots.com -webtv.net -wegelin.ch -wellsfargo.com -wellsfargoemail.com -westernunion.ca -westernunion.com -westernunion.fr -westernunion.se -westpac.com.au -westpac.co.nz -wir.ch -wordpress.com -worldbank.org -worldpay.com -wsj.com -wvb.de -xmlsoap.org -yacht.nl -yahoo.ca -yahoo.co.jp -yahoo.co.kr -yahoo.com -yahoo.com.br -yahoo.co.uk -yahoogroups.com -yandex.net -yandex.ru -ybonline.co.uk -yimg.com -yopi.de -yorkshirebank.co.uk -yourbankcard.com -yoursite.com -youtube.com -zagbank.ca -zdnet.com -zenithbank.com -zkb.ch -zugerkb.ch -vistaprint.dk -vistaprint.com -anpdm.com -dovecot.org -exacttarget.com -github.com -isc.org -# list-manage1.com # grey -# list-manage2.com # grey -# list-manage.com # grey -lists.isc.org -lists.roundcube.net -svn.apache.org -taggedmail.com -tumblr.com diff --git a/resources/config/sieve/bounce.sieve b/resources/config/sieve/bounce.sieve index 576bceeb..f14988de 100644 --- a/resources/config/sieve/bounce.sieve +++ b/resources/config/sieve/bounce.sieve @@ -1,23 +1,21 @@ -let "subject" "to_lowercase(header.subject)"; - -if eval "(contains(subject, 'delivery') && - (contains(subject, 'failed') || - contains(subject, 'report') || - contains(subject, 'status') || - contains(subject, 'warning'))) || - (contains(subject, 'failure') && - (contains(subject, 'delivery') || - contains(subject, 'notice') || - contains(subject, 'mail') )) || - (contains(subject, 'delivered') && - (contains(subject, 'couldn\\'t be') || - contains(subject, 'could not be') || - contains(subject, 'hasn\\'t been') || - contains(subject, 'has not been'))) || - contains(subject, 'returned mail') || - contains(subject, 'undeliverable') || - contains(subject, 'undelivered')" { +if eval "(contains(subject_lc, 'delivery') && + (contains(subject_lc, 'failed') || + contains(subject_lc, 'report') || + contains(subject_lc, 'status') || + contains(subject_lc, 'warning'))) || + (contains(subject_lc, 'failure') && + (contains(subject_lc, 'delivery') || + contains(subject_lc, 'notice') || + contains(subject_lc, 'mail') )) || + (contains(subject_lc, 'delivered') && + (contains(subject_lc, 'couldn\\'t be') || + contains(subject_lc, 'could not be') || + contains(subject_lc, 'hasn\\'t been') || + contains(subject_lc, 'has not been'))) || + contains(subject_lc, 'returned mail') || + contains(subject_lc, 'undeliverable') || + contains(subject_lc, 'undelivered')" { # Subject contains words or phrases typical for DSN let "t.SUBJ_BOUNCE_WORDS" "1"; } diff --git a/resources/config/sieve/dmarc.sieve b/resources/config/sieve/dmarc.sieve new file mode 100644 index 00000000..dd0f3995 --- /dev/null +++ b/resources/config/sieve/dmarc.sieve @@ -0,0 +1,91 @@ +if eval "env.spf.result == 'pass'" { + let "t.R_SPF_ALLOW" "1"; +} elsif eval "env.spf.result == 'fail'" { + let "t.R_SPF_FAIL" "1"; +} elsif eval "env.spf.result == 'softfail'" { + let "t.R_SPF_SOFTFAIL" "1"; +} elsif eval "env.spf.result == 'neutral'" { + let "t.R_SPF_NEUTRAL" "1"; +} elsif eval "env.spf.result == 'temperror'" { + let "t.R_SPF_DNSFAIL" "1"; +} elsif eval "env.spf.result == 'permerror'" { + let "t.R_SPF_PERMFAIL" "1"; +} else { + let "t.R_SPF_NA" "1"; +} + +if eval "env.dkim.result == 'pass'" { + let "t.R_DKIM_ALLOW" "1"; +} elsif eval "env.dkim.result == 'fail'" { + let "t.R_DKIM_REJECT" "1"; +} elsif eval "env.dkim.result == 'temperror'" { + let "t.R_DKIM_TEMPFAIL" "1"; +} elsif eval "env.dkim.result == 'permerror'" { + let "t.R_DKIM_PERMFAIL" "1"; +} else { + let "t.R_DKIM_NA" "1"; +} + +if eval "env.arc.result == 'pass'" { + let "t.ARC_ALLOW" "1"; +} elsif eval "env.arc.result == 'fail'" { + let "t.ARC_REJECT" "1"; +} elsif eval "env.arc.result == 'temperror'" { + let "t.ARC_DNSFAIL" "1"; +} elsif eval "env.arc.result == 'permerror'" { + let "t.ARC_INVALID" "1"; +} else { + let "t.ARC_NA" "1"; +} + +if eval "env.dmarc.result == 'pass'" { + let "t.DMARC_POLICY_ALLOW" "1"; +} elsif eval "env.dmarc.result == 'temperror'" { + let "t.DMARC_DNSFAIL" "1"; +} elsif eval "env.dmarc.result == 'permerror'" { + let "t.DMARC_BAD_POLICY" "1"; +} elsif eval "env.dmarc.result == 'fail'" { + if eval "env.dmarc.policy == 'quarantine'" { + let "t.DMARC_POLICY_QUARANTINE" "1"; + } elsif eval "env.dmarc.policy == 'reject'" { + let "t.DMARC_POLICY_REJECT" "1"; + } else { + let "t.DMARC_POLICY_SOFTFAIL" "1"; + } +} elsif eval "env.dmarc.result == ''" { + let "t.DMARC_NA" "1"; +} + +if eval "header.DKIM-Signature.exists" { + let "t.DKIM_SIGNED" "1"; + if eval "header.ARC-Seal.exists" { + let "t.ARC_SIGNED" "1"; + } +} + +# Check allowlists +if eval "lookup('spam/dmarc-allow', from_domain)" { + if eval "t.DMARC_POLICY_ALLOW" { + let "t.ALLOWLIST_DMARC" "1"; + } else { + let "t.BLOCKLIST_DMARC" "1"; + } +} elsif eval "lookup('spam/spf-dkim-allow', from_domain)" { + let "is_dkim_pass" "contains(env.dkim.domains, from_domain) || t.ARC_ALLOW"; + + if eval "is_dkim_pass && t.R_SPF_ALLOW" { + let "t.ALLOWLIST_SPF_DKIM" "1"; + } elsif eval "is_dkim_pass" { + let "t.ALLOWLIST_DKIM" "1"; + if eval "!t.R_SPF_DNSFAIL" { + let "t.BLOCKLIST_SPF" "1"; + } + } elsif eval "t.R_SPF_ALLOW" { + let "t.ALLOWLIST_SPF" "1"; + if eval "!t.R_DKIM_TEMPFAIL" { + let "t.BLOCKLIST_DKIM" "1"; + } + } elsif eval "!t.R_SPF_DNSFAIL && !t.R_DKIM_TEMPFAIL" { + let "t.BLOCKLIST_SPF_DKIM" "1"; + } +} diff --git a/resources/config/sieve/from.sieve b/resources/config/sieve/from.sieve index 93f624ef..79d546e2 100644 --- a/resources/config/sieve/from.sieve +++ b/resources/config/sieve/from.sieve @@ -2,11 +2,6 @@ let "from_count" "count(header.from[*].raw)"; let "service_accounts" "['www-data', 'anonymous', 'ftp', 'apache', 'nobody', 'guest', 'nginx', 'web', 'www']"; if eval "from_count > 0" { - let "from_name" "to_lowercase(trim(header.from.name))"; - let "from_addr" "to_lowercase(trim(header.from.addr))"; - let "from_local" "email_part(from_addr, 'local')"; - let "from_domain" "email_part(from_addr, 'domain')"; - let "from_domain_sld" "domain_part(from_domain, 'sld')"; let "from_raw" "to_lowercase(header.from.raw)"; if eval "from_count > 1" { @@ -42,8 +37,8 @@ if eval "from_count > 0" { if eval "is_email(from_name)" { let "from_name_sld" "domain_part(email_part(from_name, 'domain'), 'sld')"; if eval "(!t.FROM_INVALID && from_domain_sld != from_name_sld) || - (!is_empty(envelope.from) && domain_part(email_part(envelope.from, 'domain'), 'sld') != from_name_sld) || - (is_empty(envelope.from) && domain_part(env.helo_domain, 'sld') != from_name_sld)" { + (!is_empty(envelope.from) && envfrom_domain_sld != from_name_sld) || + (is_empty(envelope.from) && helo_domain_sld != from_name_sld)" { let "t.SPOOF_DISPLAY_NAME" "1"; } else { let "t.FROM_NEQ_DISPLAY_NAME" "1"; @@ -69,7 +64,7 @@ if eval "from_count > 0" { eq_ignore_case(from_addr, envelope.from)) || (t.HFILTER_FROM_BOUNCE && !is_empty(from_domain) && - domain_part(from_domain, 'sld') == domain_part(env.helo_domain, 'sld'))" { + from_domain_sld == helo_domain_sld)" { let "t.FROM_EQ_ENVFROM" "1"; } elsif eval "!t.FROM_INVALID" { let "t.FORGED_SENDER" "1"; @@ -80,11 +75,10 @@ if eval "from_count > 0" { let "t.TAGGED_FROM" "1"; } - let "to" "header.to:cc[*].addr[*]"; - if eval "count(to) == 1" { - if eval "eq_ignore_case(to[0], from_addr)" { + if eval "count(recipients_to) + count(recipients_cc) == 1" { + if eval "eq_ignore_case(recipients_to[0], from_addr)" { let "t.TO_EQ_FROM" "1"; - } elsif eval "eq_ignore_case(email_part(to[0], 'domain'), from_domain)" { + } elsif eval "eq_ignore_case(email_part(recipients_to[0], 'domain'), from_domain)" { let "t.TO_DOM_EQ_FROM_DOM" "1"; } } @@ -123,20 +117,24 @@ if eval "from_count > 0" { if eval "!is_empty(envelope.from)" { if eval "is_email(envelope.from)" { - if eval "contains(service_accounts, email_part(envelope.from, 'local'))" { + if eval "contains(service_accounts, envfrom_local)" { let "t.ENVFROM_SERVICE_ACCT" "1"; } } else { let "t.ENVFROM_INVALID" "1"; } - let "envfrom_domain_sld" "domain_part(email_part(envelope.from, 'domain'), 'sld')"; if eval "!is_empty(envfrom_domain_sld)" { if eval "lookup('spam/free-domains', envfrom_domain_sld)" { let "t.FREEMAIL_ENVFROM" "1"; } elsif eval "lookup('spam/disposable-domains', envfrom_domain_sld)" { let "t.DISPOSABLE_ENVFROM" "1"; } + + # Mail from no resolve to A or MX + if eval "!dns_exists(envfrom_domain, 'mx') && !dns_exists(envfrom_domain, 'ip')" { + let "t.HFILTER_FROMHOST_NORES_A_OR_MX" "1"; + } } # Read confirmation address is different to return path diff --git a/resources/config/sieve/headers.sieve b/resources/config/sieve/headers.sieve index f0f67e3e..74ed94b6 100644 --- a/resources/config/sieve/headers.sieve +++ b/resources/config/sieve/headers.sieve @@ -1,5 +1,22 @@ +# Mailing list scores +let "ml_score" "count(header.List-Id:List-Archive:List-Owner:List-Help:List-Post:X-Loop:List-Subscribe:List-Unsubscribe[*].exists) * 0.125"; +eval "print('ml_score: ' + ml_score)"; +if eval "ml_score < 1" { + if eval "header.List-Id.exists" { + let "ml_score" "ml_score + 0.50"; + } + if eval "header.List-Subscribe.exists && header.List-Unsubscribe.exists" { + let "ml_score" "ml_score + 0.25"; + } + if eval "header.Precedence.exists && (eq_ignore_case(header.Precedence, 'list') || eq_ignore_case(header.Precedence, 'bulk'))" { + let "ml_score" "ml_score + 0.25"; + } +} +if eval "ml_score >= 1" { + let "t.MAILLIST" "1"; +} - +# X-Priority if eval "header.x-priority.exists" { let "xp" "header.x-priority"; if eval "xp == 0" { diff --git a/resources/config/sieve/helo.sieve b/resources/config/sieve/helo.sieve new file mode 100644 index 00000000..870197b1 --- /dev/null +++ b/resources/config/sieve/helo.sieve @@ -0,0 +1,30 @@ +if eval "!is_ip_addr(env.helo_domain)" { + let "helo" "env.helo_domain"; + + if eval "contains(helo, '.')" { + if eval "!is_empty(env.iprev.ptr) && !eq_ignore_case(helo, env.iprev.ptr)" { + # Unknown client hostname (PTR or FCrDNS verification failed) + let "t.HFILTER_HOSTNAME_UNKNOWN" "1"; + } + if eval "!dns_exists(helo, 'ip') && !dns_exists(helo, 'mx')" { + # Helo no resolve to A or MX + let "t.HFILTER_HELO_NORES_A_OR_MX" "1"; + } + } else { + if eval "contains(helo, 'user')" { + # HELO contains 'user' + let "t.RCVD_HELO_USER" "1"; + } + + # Helo not FQDN + let "t.HFILTER_HELO_NOT_FQDN" "1"; + } +} else { + # Helo host is bare ip + let "t.HFILTER_HELO_BAREIP" "1"; + + if eval "env.helo_domain != env.remote_ip" { + # Helo A IP != hostname IP + let "t.HFILTER_HELO_IP_A" "1"; + } +} diff --git a/resources/config/sieve/html.sieve b/resources/config/sieve/html.sieve index 3ba05d4c..f64320bf 100644 --- a/resources/config/sieve/html.sieve +++ b/resources/config/sieve/html.sieve @@ -8,7 +8,7 @@ foreverypart { if eval "eq_ignore_case(header.content-type, 'text/html')" { # Tokenize HTML let "is_body_part" "is_body()"; - let "html_tokens" "tokenize_html(part.text)"; + let "html_tokens" "tokenize(part.text, 'html')"; let "html_tokens_len" "len(html_tokens)"; let "html_char_count" "0"; let "html_space_count" "0"; @@ -35,9 +35,9 @@ foreverypart { let "html_space_count" "html_space_count + count_spaces(token)"; let "text" "to_lowercase(trim(strip_prefix(token, '_')))"; - let "html_words" "html_words + len(tokenize_words(text))"; + let "html_words" "html_words + len(tokenize(text, 'words'))"; - let "uris" "tokenize_url(text, false)"; + let "uris" "tokenize(text, 'uri')"; if eval "!is_empty(uris)" { let "has_uri" "1"; diff --git a/resources/config/sieve/ip.sieve b/resources/config/sieve/ip.sieve new file mode 100644 index 00000000..542c9be6 --- /dev/null +++ b/resources/config/sieve/ip.sieve @@ -0,0 +1,22 @@ +# Reverse ip checks +if eval "env.iprev.result != ''" { + if eval "ends_with(env.iprev.result, 'error')" { + let "t.RDNS_DNSFAIL" "1"; + } elsif eval "env.iprev.result == 'fail'" { + let "t.RDNS_NONE" "1"; + } +} + +# Lookup ASN +let "asn_lookup" ""; +if eval "len(env.remote_ip.reverse) <= 15" { + let "asn_lookup" "env.remote_ip.reverse + '.origin.asn.cymru.com'"; +} else { + let "asn_lookup" "env.remote_ip.reverse + '.origin.asn6.cymru.com'"; +} +let "asn_lookup" "split(dns_query(asn_lookup, 'txt'), '|')"; +let "asn" "asn_lookup[0]"; +let "country" "asn_lookup[2]"; + +#eval "print('ASN: ' + asn + ' (' + country + ')')"; + diff --git a/resources/config/sieve/messageid.sieve b/resources/config/sieve/messageid.sieve index 77a1421e..a55f0ac7 100644 --- a/resources/config/sieve/messageid.sieve +++ b/resources/config/sieve/messageid.sieve @@ -22,15 +22,15 @@ if eval "!is_empty(mid_raw)" { } # From address present in Message-ID checks - let "from_lcase" "to_lowercase(header.from.addr)"; - if eval "is_empty(from_lcase)" { - let "from_lcase" "envelope.from"; + let "sender" "from_addr"; + if eval "is_empty(sender)" { + let "sender" "envelope.from"; } - if eval "!is_empty(from_lcase)" { - if eval "contains(mid_lcase, from_lcase)" { + if eval "!is_empty(sender)" { + if eval "contains(mid_lcase, sender)" { let "t.MID_CONTAINS_FROM" "1"; } else { - let "from_domain" "email_part(from_lcase, 'domain')"; + let "from_domain" "email_part(sender, 'domain')"; let "mid_sld" "domain_part(mid_rhs, 'sld')"; if eval "mid_rhs == from_domain" { @@ -42,12 +42,11 @@ if eval "!is_empty(mid_raw)" { } # To/Cc addresses present in Message-ID checks - let "recipients" "winnow(header.to:cc:bcc[*].addr[*])"; let "recipients_len" "count(recipients)"; let "i" "0"; while "i < recipients_len" { - let "rcpt" "to_lowercase(recipients[i])"; + let "rcpt" "recipients[i]"; let "i" "i + 1"; if eval "contains(mid_lcase, rcpt)" { let "t.MID_CONTAINS_TO" "1"; diff --git a/resources/config/sieve/mime.sieve b/resources/config/sieve/mime.sieve index 8137c610..eb8accc6 100644 --- a/resources/config/sieve/mime.sieve +++ b/resources/config/sieve/mime.sieve @@ -10,6 +10,7 @@ let "has_text_part" "0"; let "is_encrypted" "0"; foreverypart { + let "content_type" "to_lowercase(header.content-type)"; let "type" "to_lowercase(header.content-type.type)"; let "subtype" "to_lowercase(header.content-type.subtype)"; let "cte" "header.content-transfer-encoding"; @@ -41,13 +42,13 @@ foreverypart { if eval "!has_plain_part && ma_ct == 'text/plain'" { let "text_part" "part.text"; - let "text_part_words" "tokenize_words(text_part)"; - let "text_part_uris" "count(tokenize_url(text_part, true))"; + let "text_part_words" "tokenize(text_part, 'words')"; + let "text_part_uris" "count(tokenize(text_part, 'uri_strict'))"; let "has_plain_part" "1"; } elsif eval "!has_html_part && ma_ct == 'text/html'" { let "html_part" "html_to_text(part.text)"; - let "html_part_words" "tokenize_words(html_part)"; - let "html_part_uris" "count(tokenize_url(html_part, true))"; + let "html_part_words" "tokenize(html_part, 'words')"; + let "html_part_uris" "count(tokenize(html_part, 'uri_strict'))"; let "has_html_part" "1"; } } @@ -149,9 +150,61 @@ foreverypart { if eval "part_is_attachment" { # Has a MIME attachment let "t.HAS_ATTACHMENT" "1"; + + # Detect and compare mime type + let "detected_mime_type" "detect_file_type('mime')"; + if eval "!is_empty(detected_mime_type)" { + if eval "detected_mime_type == content_type" { + # Known content-type + let "t.MIME_GOOD" "1"; + } elsif eval "content_type != 'application/octet-stream'" { + # Known bad content-type + let "t.MIME_BAD" "1"; + } + } } + + # Analyze attachment name + let "attach_name" "attachment_name()"; + if eval "!is_empty(attach_name)" { + if eval "has_obscured(attach_name)" { + let "t.MIME_BAD_UNICODE" "1"; + } + let "name_parts" "rsplit(to_lowercase(attach_name), '.')"; + if eval "count(name_parts) > 1" { + let "ext_type" "lookup_map('spam/mime-types', name_parts[0])"; + if eval "!is_empty(ext_type)" { + let "ext_type_double" "lookup_map('spam/mime-types', name_parts[1])"; + if eval "contains(ext_type, 'BAD')" { + # Bad extension + if eval "contains(ext_type_double, 'BAD')" { + let "t.MIME_DOUBLE_BAD_EXTENSION" "1"; + } else { + let "t.MIME_BAD_EXTENSION" "1"; + } + } + if eval "contains(ext_type, 'AR') && contains(ext_type_double, 'AR')" { + # Archive in archive + let "t.MIME_ARCHIVE_IN_ARCHIVE" "1"; + } + + if eval "contains(ext_type, '/') && + content_type != 'application/octet-stream' && + !contains(split(ext_type, '|'), content_type)" { + # Invalid attachment mime type + let "t.MIME_BAD_ATTACHMENT" "1"; + } + } + } + } + } if eval "has_text_part && (t.ENCRYPTED_SMIME || t.SIGNED_SMIME || t.ENCRYPTED_PGP || t.SIGNED_PGP)" { let "t.BOGUS_ENCRYPTED_AND_TEXT" "1"; } + +# Check for mixed script in body +if eval "!is_single_script(text_body)" { + let "t.R_MIXED_CHARSET" "1"; +} diff --git a/resources/config/sieve/prelude.sieve b/resources/config/sieve/prelude.sieve new file mode 100644 index 00000000..74f723a4 --- /dev/null +++ b/resources/config/sieve/prelude.sieve @@ -0,0 +1,36 @@ +# Convert body to plain text +let "text_body" "body.to_text"; + +# Obtain all URLs in the body +let "body_urls" "tokenize(text_body, 'uri')"; + +# Obtain all URLs in href and src attributes +let "html_body_urls" "html_attrs(body.html, '', ['href', 'src'])"; + +# Obtain all URLs in the subject, combine them with all other URLs and remove duplicates +let "urls" "dedup(tokenize(header.subject, 'uri') + body_urls + html_body_urls)"; + +# Obtain thread name and subject +let "subject_lc" "to_lowercase(header.subject)"; +let "thread_name" "thread_name(header.subject)"; + +# Obtain all recipients +let "recipients" "to_lowercase(header.to:cc:bcc[*].addr[*])"; +let "recipients_clean" "winnow(dedup(recipients))"; +let "recipients_to" "header.to[*].addr[*]"; +let "recipients_cc" "header.cc[*].addr[*]"; + +# Obtain From parts +let "from_name" "to_lowercase(trim(header.from.name))"; +let "from_addr" "to_lowercase(trim(header.from.addr))"; +let "from_local" "email_part(from_addr, 'local')"; +let "from_domain" "email_part(from_addr, 'domain')"; +let "from_domain_sld" "domain_part(from_domain, 'sld')"; + +# Obtain Envelope From parts +let "envfrom_local" "email_part(envelope.from, 'local')"; +let "envfrom_domain" "email_part(envelope.from, 'domain')"; +let "envfrom_domain_sld" "domain_part(envfrom_domain, 'sld')"; + +# Obtain HELO domain SLD +let "helo_domain_sld" "domain_part(env.helo_domain, 'sld')"; diff --git a/resources/config/sieve/rbl.sieve b/resources/config/sieve/rbl.sieve new file mode 100644 index 00000000..9502f08e --- /dev/null +++ b/resources/config/sieve/rbl.sieve @@ -0,0 +1,339 @@ + +# Validate IP addresses +let "ip_addresses" "dedup(winnow([ env.remote_ip ] + header.received[*].rcvd.ip + header.received[*].rcvd.from.ip + header.received[*].rcvd.by.ip))"; +let "ip_addresses_len" "count(ip_addresses)"; +let "i" "0"; + +while "i < ip_addresses_len" { + let "ip_address" "ip_addresses[i]"; + let "is_from_addr" "i == 0"; + let "i" "i + 1"; + + if eval "ip_address == '127.0.0.1' || ip_address == '::1'" { + continue; + } + + # Do not check more than 10 IP addresses + if eval "i >= 10" { + break; + } + + let "ip_reverse" "ip_reverse_name(ip_address)"; + let "is_ip_v4" "len(ip_reverse) <= 15"; + + # Query SPAMHAUS + let "result" "rsplit_once(dns_query(ip_reverse + '.zen.spamhaus.org', 'ipv4')[0], '.')"; + if eval "result[0] == '127.0.0'" { + let "result" "result[1]"; + + if eval "result == 2" { + if eval "is_from_addr" { + let "t.RBL_SPAMHAUS_SBL" "1"; + } else { + let "t.RECEIVED_SPAMHAUS_SBL" "1"; + } + } elsif eval "result == 3" { + if eval "is_from_addr" { + let "t.RBL_SPAMHAUS_CSS" "1"; + } else { + let "t.RECEIVED_SPAMHAUS_CSS" "1"; + } + } elsif eval "result >= 4 && result <= 7" { + if eval "is_from_addr" { + let "t.RBL_SPAMHAUS_XBL" "1"; + } else { + let "t.RECEIVED_SPAMHAUS_XBL" "1"; + } + } elsif eval "result == 9" { + if eval "is_from_addr" { + let "t.RBL_SPAMHAUS_DROP" "1"; + } else { + let "t.RECEIVED_SPAMHAUS_PBL" "1"; + } + } elsif eval "result == 10 || result == 11" { + if eval "is_from_addr" { + let "t.RBL_SPAMHAUS_PBL" "1"; + } else { + let "t.RECEIVED_SPAMHAUS_PBL" "1"; + } + } elsif eval "result == 254" { + if eval "is_from_addr" { + let "t.RBL_SPAMHAUS_BLOCKED_OPENRESOLVER" "1"; + } else { + let "t.RECEIVED_SPAMHAUS_BLOCKED_OPENRESOLVER" "1"; + } + } elsif eval "result == 255" { + if eval "is_from_addr" { + let "t.RBL_SPAMHAUS_BLOCKED" "1"; + } else { + let "t.RECEIVED_SPAMHAUS_BLOCKED" "1"; + } + } else { + # Unrecognized result + let "t.RBL_SPAMHAUS" "1"; + } + } + + if eval "is_from_addr" { + # Query IP reputation at Mailspike + let "result" "rsplit_once(dns_query(ip_reverse + '.rep.mailspike.net', 'ipv4')[0], '.')"; + if eval "result[0] == '127.0.0'" { + let "result" "result[1]"; + + if eval "result == 10" { + let "t.RBL_MAILSPIKE_WORST" "1"; + } elsif eval "result == 11" { + let "t.RBL_MAILSPIKE_VERYBAD" "1"; + } elsif eval "result == 12" { + let "t.RBL_MAILSPIKE_BAD" "1"; + } elsif eval "result >= 13 && result <= 16" { + let "t.RWL_MAILSPIKE_NEUTRAL" "1"; + } elsif eval "result == 17" { + let "t.RWL_MAILSPIKE_POSSIBLE" "1"; + } elsif eval "result == 18" { + let "t.RWL_MAILSPIKE_GOOD" "1"; + } elsif eval "result == 19" { + let "t.RWL_MAILSPIKE_VERYGOOD" "1"; + } elsif eval "result == 20" { + let "t.RWL_MAILSPIKE_EXCELLENT" "1"; + } + } + + # Query SenderScore + if eval "dns_exists(ip_reverse + '.bl.score.senderscore.com', 'ipv4')" { + let "t.RBL_SENDERSCORE" "1"; + } + + # Query SpamEatingMonkey + if eval "is_ip_v4 && dns_exists(ip_reverse + '.bl.spameatingmonkey.net', 'ipv4')" { + let "t.RBL_SEM" "1"; + } elsif eval "!is_ip_v4 && dns_exists(ip_reverse + '.bl.ipv6.spameatingmonkey.net', 'ipv4')" { + let "t.RBL_SEM_IPV6" "1"; + } + + # Query VirusFree + if eval "dns_query(ip_reverse + '.bip.virusfree.cz', 'ipv4')[0] == '127.0.0.2'" { + let "t.RBL_VIRUSFREE_BOTNET" "1"; + } + + # Query NiX + if eval "dns_exists(ip_reverse + '.ix.dnsbl.manitu.net', 'ipv4')" { + let "t.RBL_NIXSPAM" "1"; + } + + # Query Spamcop + if eval "dns_exists(ip_reverse + '.bl.spamcop.net', 'ipv4')" { + let "t.RBL_SPAMCOP" "1"; + } + + # Query Barracuda + if eval "dns_exists(ip_reverse + '.b.barracudacentral.org', 'ipv4')" { + let "t.RBL_BARRACUDA" "1"; + } + } + + # Query Blocklist.de + if eval "dns_exists(ip_reverse + '.bl.blocklist.de', 'ipv4')" { + if eval "is_from_addr" { + let "t.RBL_BLOCKLISTDE" "1"; + } else { + let "t.RECEIVED_BLOCKLISTDE" "1"; + } + } + + # Query DNSWL + let "result" "rsplit_once(dns_query(ip_reverse + '.list.dnswl.org', 'ipv4')[0], '.')"; + if eval "starts_with(result[0], '127.0.')" { + let "result" "result[1]"; + + if eval "result == 0" { + let "t.RCVD_IN_DNSWL_NONE" "1"; + } elsif eval "result == 1" { + let "t.RCVD_IN_DNSWL_LOW" "1"; + } elsif eval "result == 2" { + let "t.RCVD_IN_DNSWL_MED" "1"; + } elsif eval "result == 3" { + let "t.RCVD_IN_DNSWL_HI" "1"; + } elsif eval "result == 255" { + let "t.DNSWL_BLOCKED" "1"; + } + } +} + +# Validate domain names +let "emails" "dedup(winnow(to_lowercase([header.from, header.reply-to, envelope.from] + tokenize(text_body, 'email'))))"; +let "emails_len" "count(emails)"; +let "domains" "dedup(winnow(to_lowercase([ env.helo_domain, env.iprev.ptr ] + email_part(emails, 'domain') + puny_decode(uri_part(urls, 'host')))))"; +let "domains_len" "count(domains)"; +let "i" "0"; + +while "i < domains_len" { + let "domain" "domains[i]"; + let "i" "i + 1"; + + if eval "!contains(domain, '.') || is_ip_addr(domain)" { + continue; + } + + # Do not check more than 10 domain names + if eval "i >= 10" { + break; + } + + # Query SpamHaus DBL + let "result" "rsplit_once(dns_query(domain + '.dbl.spamhaus.org', 'ipv4')[0], '.')"; + if eval "result[0] == '127.0.0'" { + let "result" "result[1]"; + + if eval "result == 2" { + let "t.DBL_SPAM" ""; + } elsif eval "result == 4" { + let "t.DBL_PHISH" "1"; + } elsif eval "result == 5" { + let "t.DBL_MALWARE" "1"; + } elsif eval "result == 6" { + let "t.DBL_BOTNET" "1"; + } elsif eval "result == 102" { + let "t.DBL_ABUSE" "1"; + } elsif eval "result == 103" { + let "t.DBL_ABUSE_REDIR" "1"; + } elsif eval "result == 104" { + let "t.DBL_ABUSE_PHISH" "1"; + } elsif eval "result == 105" { + let "t.DBL_ABUSE_MALWARE" "1"; + } elsif eval "result == 106" { + let "t.DBL_ABUSE_BOTNET" "1"; + } elsif eval "result == 254" { + let "t.DBL_BLOCKED_OPENRESOLVER" "1"; + } elsif eval "result == 255" { + let "t.DBL_BLOCKED" "1"; + } + } + + # Query SURBL multi + let "result" "rsplit_once(dns_query(domain + '.multi.surbl.org', 'ipv4')[0], '.')"; + if eval "result[0] == '127.0.0'" { + let "result" "result[1]"; + + if eval "result == 128" { + let "t.CRACKED_SURBL" "1"; + } elsif eval "result == 64" { + let "t.ABUSE_SURBL" "1"; + } elsif eval "result == 16" { + let "t.MW_SURBL_MULTI" "1"; + } elsif eval "result == 8" { + let "t.PH_SURBL_MULTI" "1"; + } elsif eval "result == 1" { + let "t.SURBL_BLOCKED" "1"; + } + } + + # Query URIBL multi + let "result" "rsplit_once(dns_query(domain + '.multi.uribl.com', 'ipv4')[0], '.')"; + if eval "result[0] == '127.0.0'" { + let "result" "result[1]"; + + if eval "result == 1" { + let "t.URIBL_BLOCKED" "1"; + } elsif eval "result == 2" { + let "t.URIBL_BLACK" "1"; + } elsif eval "result == 4" { + let "t.URIBL_GREY" "1"; + } elsif eval "result == 8" { + let "t.URIBL_RED" "1"; + } + } + + # Query SpamEatingMonkey URIBL + if eval "dns_query(domain + '.uribl.spameatingmonkey.net', 'ipv4')[0] == '127.0.0.2'" { + let "t.SEM_URIBL" "1"; + } + + # Query SpamEatingMonkey FRESH15 + if eval "dns_query(domain + '.fresh15.spameatingmonkey.net', 'ipv4')[0] == '127.0.0.2'" { + let "t.SEM_URIBL_FRESH15" "1"; + } + +} + +# Check DKIM domains that passed validation +let "i" "count(env.dkim.domains)"; +while "i > 0" { + let "i" "i - 1"; + + # Query DNSWL + let "result" "rsplit_once(dns_query(env.dkim.domains[i] + '.dwl.dnswl.org', 'ipv4')[0], '.')"; + if eval "result[0] == '127.0.0'" { + let "result" "result[1]"; + + if eval "result == 0" { + let "t.DWL_DNSWL_NONE" "1"; + } elsif eval "result == 1" { + let "t.DWL_DNSWL_LOW" "1"; + } elsif eval "result == 2" { + let "t.DWL_DNSWL_MED" "1"; + } elsif eval "result == 3" { + let "t.DWL_DNSWL_HI" "1"; + } elsif eval "result == 255" { + let "t.DWL_DNSWL_BLOCKED" "1"; + } + } +} + +# Validate email addresses +let "i" "0"; +while "i < emails_len" { + let "email" "emails[i]"; + let "i" "i + 1"; + + if eval "!contains(email, '@')" { + continue; + } + + # Do not check more than 10 email addresses + if eval "i >= 10" { + break; + } + + # Query MSBL EBL + let "result" "rsplit_once(dns_query(hash(email, 'sha1') + '.ebl.msbl.org', 'ipv4')[0], '.')"; + if eval "result[1] == 2 || result[1] == 3" { + if eval "result[0] == '127.0.0'" { + let "t.MSBL_EBL" "1"; + } elsif eval "result[0] == '127.0.1'" { + let "t.MSBL_EBL_GREY" "1"; + } + } +} + + +# Validate URL hashes +let "i" "0"; +let "urls_len" "count(urls)"; +while "i < urls_len" { + let "url" "urls[i]"; + let "i" "i + 1"; + + # Do not check more than 10 URLs + if eval "i >= 10" { + break; + } + + # Query SURBL HASHBL + let "result" "rsplit_once(dns_query(hash(url, 'md5') + '.hashbl.surbl.org', 'ipv4')[0], '.')"; + if eval "starts_with(result[0], '127.0.')" { + let "result" "result[1]"; + + if eval "result == 8" { + let "t.SURBL_HASHBL_PHISH" "1"; + } elsif eval "result == 16" { + let "t.SURBL_HASHBL_MALWARE" "1"; + } elsif eval "result == 64" { + let "t.SURBL_HASHBL_ABUSE" "1"; + } elsif eval "result == 128" { + let "t.SURBL_HASHBL_CRACKED" "1"; + } elsif eval "result[0] == '127.0.1'" { + let "t.SURBL_HASHBL_EMAIL" "1"; + } + } +} diff --git a/resources/config/sieve/received.sieve b/resources/config/sieve/received.sieve index 2a884d9f..3c14d57e 100644 --- a/resources/config/sieve/received.sieve +++ b/resources/config/sieve/received.sieve @@ -28,24 +28,9 @@ if eval "!is_ascii(rcvd_raw)" { let "t.RCVD_ILLEGAL_CHARS" "1"; } -# HELO contains 'user' -if eval "eq_ignore_case(env.helo_domain, 'user')" { - let "t.RCVD_HELO_USER" "1"; -} - -# Received from an IP address rather than a FQDN -if eval "is_ip_addr(env.helo_domain)" { - let "t.RCVD_IP_SPAM" "1"; -} - -# Received: HELO and IP do not match, but should -if eval "!is_empty(env.iprev.ptr) && !eq_ignore_case(env.helo_domain, env.iprev.ptr)" { - let "t.RCVD_HELO_IP_MISMATCH" "1"; -} - let "i" "0"; -let "recipients" "header.to:cc:bcc[*].addr[*]"; let "tls_count" "0"; +let "rcvd_from_ip" "0"; while "i < rcvd_count" { let "i" "i + 1"; let "helo_domain" "received_part(i, 'from')"; @@ -72,9 +57,9 @@ while "i < rcvd_count" { let "t.RCVD_HELO_USER" "1"; } - if eval "!t.RCVD_IP_SPAM && !is_empty(received_part(i, 'from.ip'))" { + if eval "!is_empty(received_part(i, 'from.ip'))" { # Received from an IP address rather than a FQDN - let "t.RCVD_IP_SPAM" "1"; + let "rcvd_from_ip" "rcvd_from_ip + 1"; } if eval "!is_empty(received_part(i, 'tls'))" { @@ -83,6 +68,21 @@ while "i < rcvd_count" { } } +if eval "rcvd_from_ip >= 2 || (rcvd_from_ip == 1 && is_ip_addr(env.helo_domain))" { + # Has two or more Received headers containing bare IP addresses + let "t.RCVD_DOUBLE_IP_SPAM" "1"; +} + +if eval "rcvd_count == 0" { + # One received header in a message (currently zero but one header will be added later by the MTA) + let "t.ONCE_RECEIVED" "1"; + + # Message has been directly delivered from MUA to local MX + if eval "header.User-Agent.exists || header.X-Mailer.exists" { + let "t.DIRECT_TO_MX" "1"; + } +} + # Received with TLS checks if eval "rcvd_count > 0 && tls_count == rcvd_count && !is_empty(env.tls.version)" { let "t.RCVD_TLS_ALL" "1"; diff --git a/resources/config/sieve/recipient.sieve b/resources/config/sieve/recipient.sieve index 93aed0b5..ad4d2640 100644 --- a/resources/config/sieve/recipient.sieve +++ b/resources/config/sieve/recipient.sieve @@ -17,9 +17,7 @@ if eval "!is_empty(to_raw)" { let "t.MISSING_TO" "1"; } -let "rcpt_addr" "dedup(to_lowercase(header.to:cc:bcc[*].addr[*]))"; -let "rcpt_addr_clean" "winnow(rcpt_addr)"; -let "rcpt_count" "count(rcpt_addr_clean)"; +let "rcpt_count" "count(recipients_clean)"; if eval "rcpt_count > 0" { if eval "rcpt_count == 1" { @@ -39,15 +37,14 @@ if eval "rcpt_count > 0" { } let "rcpt_name" "to_lowercase(header.to:cc:bcc[*].name[*])"; - let "i" "count(rcpt_addr)"; + let "i" "count(recipients)"; let "to_dn_count" "0"; let "to_dn_eq_addr_count" "0"; let "to_match_envrcpt" "0"; - let "subject" "to_lowercase(thread_name(header.subject))"; while "i != 0" { let "i" "i - 1"; - let "addr" "rcpt_addr[i]"; + let "addr" "recipients[i]"; if eval "!is_empty(addr)" { let "name" "rcpt_name[i]"; @@ -70,9 +67,9 @@ if eval "rcpt_count > 0" { # Check if the local part is present in the subject let "local_part" "email_part(addr, 'local')"; if eval "!is_empty(local_part)" { - if eval "contains(subject, addr)" { + if eval "contains(subject_lc, addr)" { let "t.RCPT_ADDR_IN_SUBJECT" "1"; - } elsif eval "len(local_part) > 3 && contains(subject, local_part)" { + } elsif eval "len(local_part) > 3 && contains(subject_lc, local_part)" { let "t.RCPT_LOCAL_IN_SUBJECT" "1"; } @@ -84,7 +81,7 @@ if eval "rcpt_count > 0" { # Check if it is an into to info if eval "!t.INFO_TO_INFO_LU && local_part == 'info' && - eq_ignore_case(email_part(header.from.addr, 'local'), 'info') && + from_local == 'info' && header.List-Unsubscribe.exists" { let "t.INFO_TO_INFO_LU" "1"; } @@ -93,15 +90,15 @@ if eval "rcpt_count > 0" { let "domain" "domain_part(email_part(addr, 'domain'), 'sld')"; if eval "!is_empty(domain)" { if eval "lookup('spam/free-domains', domain)" { - if eval "!t.FREEMAIL_TO && contains_ignore_case(header.to[*].addr[*], addr)" { + if eval "!t.FREEMAIL_TO && contains_ignore_case(recipients_to, addr)" { let "t.FREEMAIL_TO" "1"; - } elsif eval "!t.FREEMAIL_CC && contains_ignore_case(header.cc[*].addr[*], addr)" { + } elsif eval "!t.FREEMAIL_CC && contains_ignore_case(recipients_cc, addr)" { let "t.FREEMAIL_CC" "1"; } } elsif eval "lookup('spam/disposable-domains', domain)" { - if eval "!t.DISPOSABLE_TO && contains_ignore_case(header.to[*].addr[*], addr)" { + if eval "!t.DISPOSABLE_TO && contains_ignore_case(recipients_to, addr)" { let "t.DISPOSABLE_TO" "1"; - } elsif eval "!t.DISPOSABLE_CC && contains_ignore_case(header.cc[*].addr[*], addr)" { + } elsif eval "!t.DISPOSABLE_CC && contains_ignore_case(recipients_cc, addr)" { let "t.DISPOSABLE_CC" "1"; } } @@ -136,7 +133,7 @@ if eval "rcpt_count > 0" { let "i" "i - 1"; let "env_rcpt" "envelope.to[i]"; - if eval "!contains(rcpt_addr, env_rcpt) && env_rcpt != envelope.from" { + if eval "!contains(recipients, env_rcpt) && env_rcpt != envelope.from" { let "t.FORGED_RECIPIENTS" "1"; break; } @@ -147,13 +144,13 @@ if eval "rcpt_count > 0" { # Message from bounce and over 1 recipient if eval "rcpt_count > 1 && (is_empty(envelope.from) || - starts_with(envelope.from, 'postmaster@') || - starts_with(envelope.from, 'mailer-daemon@'))" { + envfrom_local == 'postmaster' || + envfrom_local == 'mailer-daemon')" { let "t.HFILTER_RCPT_BOUNCEMOREONE" "1"; } # Check for sorted recipients - if eval "rcpt_count >= 7 && sort(rcpt_addr_clean, false) == rcpt_addr_clean" { + if eval "rcpt_count >= 7 && sort(recipients_clean, false) == recipients_clean" { let "t.SORTED_RECIPS" "1"; } @@ -168,8 +165,8 @@ if eval "rcpt_count > 0" { let "j" "i"; while "j" { let "j" "j - 1"; - let "a" "rcpt_addr_clean[i]"; - let "b" "rcpt_addr_clean[j]"; + let "a" "recipients_clean[i]"; + let "b" "recipients_clean[j]"; if eval "levenshtein_distance(email_part(a, 'local'), email_part(b, 'local')) < 3" { let "hits" "hits + 1"; diff --git a/resources/config/sieve/replyto.sieve b/resources/config/sieve/replyto.sieve index 71e73819..45e41b23 100644 --- a/resources/config/sieve/replyto.sieve +++ b/resources/config/sieve/replyto.sieve @@ -1,4 +1,3 @@ - let "rto_raw" "to_lowercase(header.reply-to.raw)"; if eval "!is_empty(rto_raw)" { let "rto_addr" "to_lowercase(header.reply-to.addr)"; @@ -6,18 +5,16 @@ if eval "!is_empty(rto_raw)" { if eval "is_email(rto_addr)" { let "t.HAS_REPLYTO" "1"; - let "rto_domain" "domain_part(email_part(rto_addr, 'domain'), 'sld')"; - let "from_addr" "to_lowercase(header.from.addr)"; - let "from_domain" "domain_part(email_part(from_addr, 'domain'), 'sld')"; + let "rto_domain_sld" "domain_part(email_part(rto_addr, 'domain'), 'sld')"; if eval "eq_ignore_case(header.reply-to, header.from)" { let "t.REPLYTO_EQ_FROM" "1"; } else { - if eval "rto_domain == from_domain" { + if eval "rto_domain_sld == from_domain_sld" { let "t.REPLYTO_DOM_EQ_FROM_DOM" "1"; } else { let "is_from_list" "!is_empty(header.List-Unsubscribe:List-Id:X-To-Get-Off-This-List:X-List:Auto-Submitted[*])"; - if eval "!is_from_list && contains_ignore_case(header.to:cc:bcc[*].addr[*], rto_addr)" { + if eval "!is_from_list && contains_ignore_case(recipients_clean, rto_addr)" { let "t.REPLYTO_EQ_TO_ADDR" "1"; } else { let "t.REPLYTO_DOM_NEQ_FROM_DOM" "1"; @@ -32,7 +29,7 @@ if eval "!is_empty(rto_raw)" { while "i != 0" { let "i" "i - 1"; - if eval "domain_part(email_part(envelope.to[i], 'domain'), 'sld') == from_domain" { + if eval "domain_part(email_part(envelope.to[i], 'domain'), 'sld') == from_domain_sld" { let "found_domain" "1"; break; } @@ -49,12 +46,12 @@ if eval "!is_empty(rto_raw)" { } } - if eval "lookup('spam/free-domains', rto_domain)" { + if eval "lookup('spam/free-domains', rto_domain_sld)" { let "t.FREEMAIL_REPLYTO" "1"; - if eval "rto_domain != from_domain && lookup('spam/free-domains', from_domain)" { + if eval "rto_domain_sld != from_domain_sld && lookup('spam/free-domains', from_domain_sld)" { let "t.FREEMAIL_REPLYTO_NEQ_FROM_DOM" "1"; } - } elsif eval "lookup('spam/disposable-domains', rto_domain)" { + } elsif eval "lookup('spam/disposable-domains', rto_domain_sld)" { let "t.DISPOSABLE_REPLYTO" "1"; } diff --git a/resources/config/sieve/subject.sieve b/resources/config/sieve/subject.sieve index a9c7db01..7683c234 100644 --- a/resources/config/sieve/subject.sieve +++ b/resources/config/sieve/subject.sieve @@ -1,80 +1,78 @@ -let "thread_name" "thread_name(header.subject)"; -let "subject_lcase" "to_lowercase(header.subject)"; -let "raw_subject_lcase" "to_lowercase(header.subject.raw)"; -let "is_ascii_subject" "is_ascii(subject_lcase)"; +let "raw_subject_lc" "to_lowercase(header.subject.raw)"; +let "is_ascii_subject" "is_ascii(subject_lc)"; -if eval "len(thread_name) >= 10 && count(tokenize_words(thread_name)) > 1 && is_uppercase(thread_name)" { +if eval "len(thread_name) >= 10 && count(tokenize(thread_name, 'words')) > 1 && is_uppercase(thread_name)" { # Subject contains mostly capital letters let "t.SUBJ_ALL_CAPS" "1"; } if eval "count_chars(thread_name) > 200" { # Subject is very long - let "t.SUBJ_VERY_LONG" "1"; + let "t.LONG_SUBJ" "1"; } -if eval "!is_empty(tokenize_url(subject_lcase, true))" { +if eval "!is_empty(tokenize(subject_lc, 'uri_strict'))" { # Subject contains a URL let "t.URL_IN_SUBJECT" "1"; } -if eval "!is_ascii(raw_subject_lcase) && !env.param.smtputf8 && env.param.body != '8bitmime' && env.param.body != 'binarymime'" { +if eval "!is_ascii(raw_subject_lc) && !env.param.smtputf8 && env.param.body != '8bitmime' && env.param.body != 'binarymime'" { # Subject needs encoding let "t.SUBJECT_NEEDS_ENCODING" "1"; } -if not exists "Subject" { +if eval "!header.Subject.exists" { # Missing subject header let "t.MISSING_SUBJECT" "1"; -} elsif eval "is_empty(trim(subject_lcase))" { +} elsif eval "is_empty(trim(subject_lc))" { # Subject is empty let "t.EMPTY_SUBJECT" "1"; } -if eval "is_ascii(subject_lcase) && contains(raw_subject_lcase, '=?') && contains(raw_subject_lcase, '?=')" { - if eval "contains(raw_subject_lcase, '?q?')" { +if eval "is_ascii(subject_lc) && contains(raw_subject_lc, '=?') && contains(raw_subject_lc, '?=')" { + if eval "contains(raw_subject_lc, '?q?')" { # Subject header is unnecessarily encoded in quoted-printable let "t.SUBJ_EXCESS_QP" "1"; - } elsif eval "contains(raw_subject_lcase, '?b?')" { + } elsif eval "contains(raw_subject_lc, '?b?')" { # Subject header is unnecessarily encoded in base64 let "t.SUBJ_EXCESS_BASE64" "1"; } } -if eval "starts_with(subject_lcase, 're:') && is_empty(header.in-reply-to) && is_empty(header.references)" { +if eval "starts_with(subject_lc, 're:') && is_empty(header.in-reply-to) && is_empty(header.references)" { # Fake reply let "t.FAKE_REPLY" "1"; } -let "subject_lcase_trim" "trim_end(subject_lcase)"; -if eval "subject_lcase != subject_lcase_trim" { +let "subject_lc_trim" "trim_end(subject_lc)"; +if eval "subject_lc != subject_lc_trim" { # Subject ends with space characters let "t.SUBJECT_ENDS_SPACES" "1"; } -if eval "contains(subject_lcase, '$') || - contains(subject_lcase, '€') || - contains(subject_lcase, '£') || - contains(subject_lcase, '¥')" { +if eval "contains(subject_lc, '$') || + contains(subject_lc, '€') || + contains(subject_lc, '£') || + contains(subject_lc, '¥')" { # Subject contains currency symbols let "t.SUBJECT_HAS_CURRENCY" "1"; } -if eval "ends_with(subject_lcase_trim, '!')" { +if eval "ends_with(subject_lc_trim, '!')" { # Subject ends with an exclamation mark let "t.SUBJECT_ENDS_EXCLAIM" "1"; -} elsif eval "ends_with(subject_lcase_trim, '?')" { +} elsif eval "ends_with(subject_lc_trim, '?')" { # Subject ends with a question mark let "t.SUBJECT_ENDS_QUESTION" "1"; } -if eval "contains(subject_lcase_trim, '!')" { +if eval "contains(subject_lc_trim, '!')" { # Subject contains an exclamation mark let "t.SUBJECT_HAS_EXCLAIM" "1"; } -if eval "contains(subject_lcase_trim, '?')" { +if eval "contains(subject_lc_trim, '?')" { # Subject contains a question mark let "t.SUBJECT_HAS_QUESTION" "1"; } diff --git a/resources/config/sieve/url.sieve b/resources/config/sieve/url.sieve index 5373f8cc..2377b8a7 100644 --- a/resources/config/sieve/url.sieve +++ b/resources/config/sieve/url.sieve @@ -1,9 +1,4 @@ -let "text_body" "body.to_text"; -let "body_urls" "tokenize_url(text_body, false)"; -let "html_body_urls" "html_attrs(body.html, '', ['href', 'src'])"; -let "urls" "dedup(tokenize_url(header.subject, false) + body_urls + html_body_urls)"; - -if eval "(count(body_urls) == 1 || count(html_body_urls) == 1) && count(tokenize_words(text_body)) == 0" { +if eval "(count(body_urls) == 1 || count(html_body_urls) == 1) && count(tokenize(text_body, 'words')) == 0" { let "t.HFILTER_URL_ONLY" "1"; } @@ -62,12 +57,8 @@ while "i > 0" { if eval "!is_ip" { if eval "!is_ascii(host)" { let "host_cured" "cure_text(host)"; - if eval "host_lc != host_cured" { - if eval "dns_exists(host_cured, 'ip')" { - let "t.OMOGRAPH_URL" "1"; - } else { - let "t.CONFUSABLE_URL" "1"; - } + if eval "host_lc != host_cured && dns_exists(host_cured, 'ip')" { + let "t.OMOGRAPH_URL" "1"; } if eval "!is_single_script(host)" { @@ -106,6 +97,15 @@ while "i > 0" { # Message contains URI with a hidden path let "t.URI_HIDDEN_PATH" "1"; } + + # Phishing checks + if eval "lookup('spam/phishing-open', url)" { + let "t.PHISHED_OPENPHISH" "1"; + } + if eval "lookup('spam/phishing-tank', url)" { + let "t.PHISHED_PHISHTANK" "1"; + } + } else { # URL could not be parsed let "t.R_SUSPICIOUS_URL" "1"; diff --git a/tests/resources/smtp/antispam/dmarc.test b/tests/resources/smtp/antispam/dmarc.test new file mode 100644 index 00000000..247e9109 --- /dev/null +++ b/tests/resources/smtp/antispam/dmarc.test @@ -0,0 +1,151 @@ +expect DMARC_NA R_SPF_NA R_DKIM_NA ARC_NA + +Subject: test + +Test + + +spf.result pass +dkim.result pass +arc.result pass +dmarc.result pass +expect DKIM_SIGNED ARC_SIGNED R_DKIM_ALLOW R_SPF_ALLOW ARC_ALLOW DMARC_POLICY_ALLOW + +DKIM-Signature: abc +ARC-Seal: xyz +Subject: test + +Test + + +spf.result fail +dkim.result fail +arc.result fail +dmarc.result fail +dmarc.policy quarantine +expect R_SPF_FAIL ARC_REJECT R_DKIM_REJECT DMARC_POLICY_QUARANTINE + +Subject: test + +Test + + +spf.result neutral +dkim.result temperror +arc.result permerror +dmarc.result fail +dmarc.policy reject +expect R_DKIM_TEMPFAIL R_SPF_NEUTRAL ARC_INVALID DMARC_POLICY_REJECT + +Subject: test + +Test + + +spf.result softfail +dkim.result permerror +arc.result temperror +dmarc.result permerror +expect ARC_DNSFAIL DMARC_BAD_POLICY R_DKIM_PERMFAIL R_SPF_SOFTFAIL + +Subject: test + +Test + +dmarc.result pass +dkim.result pass +spf.result pass +expect ALLOWLIST_DMARC DMARC_POLICY_ALLOW R_DKIM_ALLOW R_SPF_ALLOW ARC_NA + +From: user@dmarc-allow.org +Subject: test + +Test + + +dmarc.result fail +dkim.result fail +spf.result fail +expect BLOCKLIST_DMARC DMARC_POLICY_SOFTFAIL R_DKIM_REJECT R_SPF_FAIL ARC_NA + +From: user@dmarc-allow.org +Subject: test + +Test + + +dkim.result pass +dkim.domains spf-dkim-allow.org +spf.result pass +expect ALLOWLIST_SPF_DKIM R_DKIM_ALLOW R_SPF_ALLOW ARC_NA DMARC_NA + +From: user@spf-dkim-allow.org +Subject: test + +Test + +dkim.result pass +spf.result pass +arc.result pass +expect ALLOWLIST_SPF_DKIM R_DKIM_ALLOW R_SPF_ALLOW ARC_ALLOW DMARC_NA + +From: user@spf-dkim-allow.org +Subject: test + +Test + +spf.result pass +dkim.result fail +expect ALLOWLIST_SPF BLOCKLIST_DKIM R_DKIM_REJECT R_SPF_ALLOW ARC_NA DMARC_NA + +From: user@spf-dkim-allow.org +Subject: test + +Test + +spf.result pass +dkim.result temperror +expect ALLOWLIST_SPF R_DKIM_TEMPFAIL R_SPF_ALLOW ARC_NA DMARC_NA + +From: user@spf-dkim-allow.org +Subject: test + +Test + +dkim.result pass +dkim.domains spf-dkim-allow.org +spf.result fail +expect BLOCKLIST_SPF ALLOWLIST_DKIM R_DKIM_ALLOW R_SPF_FAIL ARC_NA DMARC_NA + +From: user@spf-dkim-allow.org +Subject: test + +Test + +dkim.result pass +dkim.domains spf-dkim-allow.org +spf.result temperror +expect ALLOWLIST_DKIM R_DKIM_ALLOW R_SPF_DNSFAIL ARC_NA DMARC_NA + +From: user@spf-dkim-allow.org +Subject: test + +Test + +dkim.result fail +spf.result fail +expect BLOCKLIST_SPF_DKIM R_DKIM_REJECT R_SPF_FAIL ARC_NA DMARC_NA + +From: user@spf-dkim-allow.org +Subject: test + +Test + +dkim.result temperror +spf.result temperror +expect R_DKIM_TEMPFAIL R_SPF_DNSFAIL ARC_NA DMARC_NA + +From: user@spf-dkim-allow.org +Subject: test + +Test diff --git a/tests/resources/smtp/antispam/from.test b/tests/resources/smtp/antispam/from.test index 237a3002..6f115c6f 100644 --- a/tests/resources/smtp/antispam/from.test +++ b/tests/resources/smtp/antispam/from.test @@ -1,14 +1,14 @@ expect MISSING_FROM -X-From: hello@world.com +X-From: hello@domain.org Test -envelope_from hello@world.com +envelope_from hello@domain.org expect MULTIPLE_FROM FROM_EQ_ENVFROM FROM_NO_DN -From: hello@world.com -From: hello@world.com +From: hello@domain.org +From: hello@domain.org Test @@ -19,52 +19,52 @@ From: test Test -envelope_from www-data@domain.com +envelope_from www-data@domain.org expect FROM_SERVICE_ACCT ENVFROM_SERVICE_ACCT FROM_HAS_DN FROM_EQ_ENVFROM -From: "WWW DATA" +From: "WWW DATA" Test -envelope_from hello@world.com +envelope_from hello@domain.org expect FROM_DN_EQ_ADDR FROM_EQ_ENVFROM -From: "hello@world.com" +From: "hello@domain.org" Test -envelope_from hello@world.com +envelope_from hello@domain.org expect SPOOF_DISPLAY_NAME FROM_EQ_ENVFROM FROM_HAS_DN -From: "hello@otherworld.com" +From: "hello@otherdomain.org" Test -envelope_from hello@world.co.uk +envelope_from hello@domain.co.uk expect FROM_NEQ_DISPLAY_NAME FROM_EQ_ENVFROM FROM_HAS_DN -From: "hello@other.world.co.uk" +From: "hello@other.domain.co.uk" Test -helo_domain mx.world.co.uk +helo_domain mx.domain.co.uk expect FROM_EQ_ENVFROM FROM_NEQ_DISPLAY_NAME FROM_HAS_DN HFILTER_FROM_BOUNCE -From: "postmaster@mx.world.co.uk" +From: "postmaster@mx.domain.co.uk" Test -helo_domain mx.world.co.uk +helo_domain mx.domain.co.uk expect FROM_EQ_ENVFROM FROM_HAS_DN HFILTER_FROM_BOUNCE -From: "Mailer Daemon" +From: "Mailer Daemon" Test -envelope_from mrspammer@domain.com +envelope_from mrspammer@domain.org expect FROM_NAME_HAS_TITLE FROM_NAME_EXCESS_SPACE FROM_EQ_ENVFROM FROM_HAS_DN -From: "Mr. Money Maker" +From: "Mr. Money Maker" Test @@ -172,3 +172,10 @@ expect DISPOSABLE_ENVFROM DISPOSABLE_FROM FROM_EQ_ENVFROM FROM_NO_DN From: hello@custom.disposable.org Test + +envelope_from hello@nomx.org +expect HFILTER_FROMHOST_NORES_A_OR_MX FROM_EQ_ENVFROM FROM_NO_DN + +From: hello@nomx.org + +Test diff --git a/tests/resources/smtp/antispam/headers.test b/tests/resources/smtp/antispam/headers.test index fa1eea47..c496aac1 100644 --- a/tests/resources/smtp/antispam/headers.test +++ b/tests/resources/smtp/antispam/headers.test @@ -56,3 +56,24 @@ expect HEADER_EMPTY_DELIMITER Subject:test Test + +expect MAILLIST + +List-Archive: 1 +List-Owner: 1 +List-Help: 1 +List-Post: 1 +X-Loop: 1 +List-Id: 1 +Subject: test + +Test + +expect MAILLIST HAS_LIST_UNSUB + +List-Id: 1 +List-Subscribe: 1 +List-Unsubscribe: 1 +Subject: test + +Test diff --git a/tests/resources/smtp/antispam/helo.test b/tests/resources/smtp/antispam/helo.test new file mode 100644 index 00000000..046d00fb --- /dev/null +++ b/tests/resources/smtp/antispam/helo.test @@ -0,0 +1,65 @@ +helo_domain localhost +expect HFILTER_HELO_NOT_FQDN + +Subject: test + +test + +helo_domain user +expect RCVD_HELO_USER HFILTER_HELO_NOT_FQDN + +Subject: test + +test + +helo_domain 8.8.8.8 +remote_ip 8.8.8.8 +expect HFILTER_HELO_BAREIP + +Subject: test + +test + +helo_domain 8.8.8.8 +remote_ip 1.1.1.1 +expect HFILTER_HELO_IP_A HFILTER_HELO_BAREIP + +Subject: test + +test + +helo_domain domain.org +iprev.ptr domain.org +remote_ip 1.1.1.1 +expect + +Subject: test + +test + +helo_domain domain.org +iprev.ptr otherdomain.org +remote_ip 1.1.1.1 +expect HFILTER_HOSTNAME_UNKNOWN + +Subject: test + +test + +helo_domain otherdomain.org +iprev.ptr otherdomain.org +remote_ip 1.1.1.1 +expect HFILTER_HELO_NORES_A_OR_MX + +Subject: test + +test + +helo_domain otherdomain.org +iprev.ptr otherdomain.net +remote_ip 1.1.1.1 +expect HFILTER_HELO_NORES_A_OR_MX HFILTER_HOSTNAME_UNKNOWN + +Subject: test + +test diff --git a/tests/resources/smtp/antispam/ip.test b/tests/resources/smtp/antispam/ip.test new file mode 100644 index 00000000..12ecb160 --- /dev/null +++ b/tests/resources/smtp/antispam/ip.test @@ -0,0 +1,17 @@ +remote_ip 8.8.8.8 +iprev.result temperror +expect RDNS_DNSFAIL + +Subject: test + +Test + + +remote_ip 8.8.8.8 +iprev.result fail +expect RDNS_NONE + +Subject: test + +Test + diff --git a/tests/resources/smtp/antispam/mime.test b/tests/resources/smtp/antispam/mime.test index 22a8ebee..07c925ba 100644 --- a/tests/resources/smtp/antispam/mime.test +++ b/tests/resources/smtp/antispam/mime.test @@ -352,3 +352,146 @@ Content-Transfer-Encoding: 7bit this is a test --boundary-- + +expect R_MIXED_CHARSET + +Content-Type: text/plain; charset="utf-8" +Content-Transfer-Encoding: 8bit +MIME-Version: 1.0 + +Tést 孔子 + + +expect MIME_BAD_EXTENSION MIME_GOOD HAS_ATTACHMENT + +MIME-Version: 1.0 +Content-Type: multipart/mixed; + boundary="boundary" + +--boundary +Content-Type: text/plain; charset="utf-8" +Content-Transfer-Encoding: 7bit + +simple text + +--boundary +Content-Type: text/html; charset="utf-8" +Content-Disposition: attachment; filename="test.html" +Content-Transfer-Encoding: 8bit + + +

hello world

+ +--boundary-- + + +expect MIME_BAD_ATTACHMENT HAS_ATTACHMENT + +MIME-Version: 1.0 +Content-Type: multipart/mixed; + boundary="boundary" + +--boundary +Content-Type: text/plain; charset="utf-8" +Content-Transfer-Encoding: 7bit + +simple text + +--boundary +Content-Type: text/x-plain; charset="utf-8" +Content-Disposition: attachment; filename="test.txt" +Content-Transfer-Encoding: 8bit + +hello world +--boundary-- + + +expect HAS_ATTACHMENT + +MIME-Version: 1.0 +Content-Type: multipart/mixed; + boundary="boundary" + +--boundary +Content-Type: text/plain; charset="utf-8" +Content-Transfer-Encoding: 7bit + +simple text + +--boundary +Content-Type: text/plain; charset="utf-8" +Content-Disposition: attachment; filename="test.txt" +Content-Transfer-Encoding: 8bit + +hello world +--boundary-- + + +expect MIME_DOUBLE_BAD_EXTENSION MIME_GOOD HAS_ATTACHMENT + +MIME-Version: 1.0 +Content-Type: multipart/mixed; + boundary="boundary" + +--boundary +Content-Type: text/plain; charset="utf-8" +Content-Transfer-Encoding: 7bit + +simple text + +--boundary +Content-Type: text/html; charset="utf-8" +Content-Disposition: attachment; filename="test.html.html" +Content-Transfer-Encoding: 8bit + + +

hello world

+ +--boundary-- + + +expect MIME_ARCHIVE_IN_ARCHIVE MIME_GOOD HAS_ATTACHMENT + +MIME-Version: 1.0 +Content-Type: multipart/mixed; + boundary="boundary" + +--boundary +Content-Type: text/plain; charset="utf-8" +Content-Transfer-Encoding: 7bit + +simple text + +--boundary +Content-Type: application/zip +Content-Disposition: attachment; filename="test.zip.zip" +Content-Transfer-Encoding: base64 + +UEsDBAoAAAAAALN6RlcAAAAAAAAAAAAAAAAIABwAdGVzdC5iaW5VVAkAA+IJI +GXiCSBldXgLAAEE9QEAAAQUAAAAUEsBAh4DCgAAAAAAs3pGVwAAAAAAAAAAAA +AAAAgAGAAAAAAAAAAAAKSBAAAAAHRlc3QuYmluVVQFAAPiCSBldXgLAAEE9QE +AAAQUAAAAUEsFBgAAAAABAAEATgAAAEIAAAAAAA== +--boundary-- + +expect MIME_BAD HAS_ATTACHMENT + +MIME-Version: 1.0 +Content-Type: multipart/mixed; + boundary="boundary" + +--boundary +Content-Type: text/plain; charset="utf-8" +Content-Transfer-Encoding: 7bit + +simple text + +--boundary +Content-Type: image/png +Content-Disposition: attachment; filename="test.png" +Content-Transfer-Encoding: base64 + +UEsDBAoAAAAAALN6RlcAAAAAAAAAAAAAAAAIABwAdGVzdC5iaW5VVAkAA+IJI +GXiCSBldXgLAAEE9QEAAAQUAAAAUEsBAh4DCgAAAAAAs3pGVwAAAAAAAAAAAA +AAAAgAGAAAAAAAAAAAAKSBAAAAAHRlc3QuYmluVVQFAAPiCSBldXgLAAEE9QE +AAAQUAAAAUEsFBgAAAAABAAEATgAAAEIAAAAAAA== +--boundary-- diff --git a/tests/resources/smtp/antispam/rbl.test b/tests/resources/smtp/antispam/rbl.test new file mode 100644 index 00000000..cc5a47f3 --- /dev/null +++ b/tests/resources/smtp/antispam/rbl.test @@ -0,0 +1,30 @@ +remote_ip 192.168.0.1 +expect RCVD_IN_DNSWL_LOW RBL_SPAMHAUS + +Subject: test + +test + + +remote_ip 192.168.0.2 +expect RBL_SENDERSCORE RBL_NIXSPAM RBL_SEM RBL_SPAMHAUS_SBL RBL_BARRACUDA RBL_BLOCKLISTDE RBL_VIRUSFREE_BOTNET RBL_SPAMCOP RCVD_IN_DNSWL_MED + +Subject: test + +test + + +remote_ip 192.168.0.14 +expect RWL_MAILSPIKE_NEUTRAL RECEIVED_SPAMHAUS_SBL RBL_SPAMHAUS RECEIVED_SPAMHAUS_XBL RECEIVED_BLOCKLISTDE RCVD_IN_DNSWL_MED + +Received: from Agni (localhost [192.168.0.5]) (TLS: TLSv1/SSLv3, 168bits,DES-CBC3-SHA) by agni.forevermore.net + with esmtp; Mon, 28 Oct 2002 14:48:52 -0800 +Received: from [192.168.0.14] (79.sub-174-252-72.myvzw.com [192.168.0.8]) by mx.google.com + with ESMTPS id m16sm345129qck.28.2011.06.15.07.42.02 (version=TLSv1/SSLv3 cipher=OTHER); Wed, 15 Jun 2011 07:42:08 -0700 (PDT) +Received: from user (192.168.0.2) by DB6PR07MB3384.eurprd07.prod.outlook.com ([192.168.0.2]) + with Microsoft SMTP Server (version=TLS1_2, cipher=TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384) id 15.20.1143.11; Thu, 13 Sep 2018 14:47:44 +0000 +Subject: test + +test + + diff --git a/tests/resources/smtp/antispam/received.test b/tests/resources/smtp/antispam/received.test index afdc7bb1..8f430440 100644 --- a/tests/resources/smtp/antispam/received.test +++ b/tests/resources/smtp/antispam/received.test @@ -29,39 +29,10 @@ Received: from BAY0-HMR08.bay0.hótmail.com (bay0-hmr08.bay0.hótmail.com [65.54 with ESMTP id h2DBpvs24047 for ; Thu, 13 Mar 2003 11:51:57 GMT test - -helo_domain user -expect RCVD_HELO_USER RCVD_COUNT_ZERO RCVD_NO_TLS_LAST -Subject: test - -test - -helo_domain 100.21.30.40 -expect RCVD_IP_SPAM RCVD_COUNT_ZERO RCVD_NO_TLS_LAST - -Subject: test - -test - -iprev.ptr host.otherdomain.com -helo_domain HOST.DOMAIN.COM -expect RCVD_HELO_IP_MISMATCH RCVD_COUNT_ZERO RCVD_NO_TLS_LAST - -Subject: test - -test - -iprev.ptr host.domain.com -helo_domain HOST.DOMAIN.COM -expect RCVD_COUNT_ZERO RCVD_NO_TLS_LAST - -Subject: test - -test tls.version TLVv1.3 -expect RCVD_TLS_ALL RCVD_HELO_USER RCVD_IP_SPAM FORGED_RCVD_TRAIL PREVIOUSLY_DELIVERED RCVD_COUNT_FIVE +expect RCVD_TLS_ALL RCVD_HELO_USER RCVD_DOUBLE_IP_SPAM FORGED_RCVD_TRAIL PREVIOUSLY_DELIVERED RCVD_COUNT_FIVE Received: from Agni (localhost [::ffff:127.0.0.1]) (TLS: TLSv1/SSLv3, 168bits,DES-CBC3-SHA) by agni.forevermore.net with esmtp; Mon, 28 Oct 2002 14:48:52 -0800 @@ -69,9 +40,18 @@ Received: from [10.231.252.223] (79.sub-174-252-72.myvzw.com [174.252.72.79]) by with ESMTPS id m16sm345129qck.28.2011.06.15.07.42.02 (version=TLSv1/SSLv3 cipher=OTHER); Wed, 15 Jun 2011 07:42:08 -0700 (PDT) Received: from user (10.175.233.33) by DB6PR07MB3384.eurprd07.prod.outlook.com (10.175.234.11) with Microsoft SMTP Server (version=TLS1_2, cipher=TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384) id 15.20.1143.11; Thu, 13 Sep 2018 14:47:44 +0000 -Received: from wmail.tana.it (wmail.tana.it [94.198.96.74]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange ECDHE (P-256) server-signature RSA-PSS (2048 bits) server-digest SHA256) (No client certificate requested) +Received: from [94.198.96.74] (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange ECDHE (P-256) server-signature RSA-PSS (2048 bits) server-digest SHA256) (No client certificate requested) by ietfa.amsl.com (Postfix) with ESMTPS id 10B7AC151535 for ; Mon, 28 Aug 2023 02:21:23 -0700 (PDT) To: user@domain.com Subject: test test + + +expect DIRECT_TO_MX ONCE_RECEIVED RCVD_COUNT_ZERO RCVD_NO_TLS_LAST + +To: user@domain.com +X-Mailer: MUA +Subject: test + +test diff --git a/tests/resources/smtp/antispam/subject.test b/tests/resources/smtp/antispam/subject.test index d0896762..945015e0 100644 --- a/tests/resources/smtp/antispam/subject.test +++ b/tests/resources/smtp/antispam/subject.test @@ -4,7 +4,7 @@ Subject: HELLO WORLD Test -expect SUBJ_VERY_LONG +expect LONG_SUBJ Subject: this is an extremely long subject line that should be truncated to 80 characters and folded diff --git a/tests/resources/smtp/antispam/url.test b/tests/resources/smtp/antispam/url.test index 56a5d39e..a8489afb 100644 --- a/tests/resources/smtp/antispam/url.test +++ b/tests/resources/smtp/antispam/url.test @@ -16,13 +16,13 @@ Subject: test my site is https://192.168.1.1 -expect CONFUSABLE_URL +expect OMOGRAPH_URL Subject: test my site is https://xn--youtue-tg7b.com -expect R_MIXED_CHARSET_URL CONFUSABLE_URL +expect R_MIXED_CHARSET_URL Subject: test @@ -40,9 +40,9 @@ Subject: nested redirect login to https://redirect.com/?https://redirect.org/?https://redirect.net/?https://redirect.io/?https://redirect.me/?https://redirect.com -expect REDIRECTOR_URL CONFUSABLE_URL +expect REDIRECTOR_URL OMOGRAPH_URL -Subject: redirect to confusable +Subject: redirect to omograph login to https://www.redirect.com/?https://xn--twiter-507b.com @@ -73,9 +73,12 @@ Subject: plain test http://url.com/Well-known/../assetlinks.json http://wp.com/WP-content/content.pdf + -expect REDIRECTOR_URL +expect PHISHED_OPENPHISH PHISHED_PHISHTANK -Subject: test +Subject: plain test + +https://phishing-open.org +https://phishing-tank.com -go to my site https://tinyurl.com/4fbkcb6m diff --git a/tests/resources/smtp/lists/disposable-domains.txt b/tests/resources/smtp/lists/disposable-domains.txt deleted file mode 100644 index b28ff8da..00000000 --- a/tests/resources/smtp/lists/disposable-domains.txt +++ /dev/null @@ -1,3 +0,0 @@ -# Disposable domains -guerrillamail.com -*.disposable.org diff --git a/tests/resources/smtp/lists/free-domains.txt b/tests/resources/smtp/lists/free-domains.txt deleted file mode 100644 index 7fc45cab..00000000 --- a/tests/resources/smtp/lists/free-domains.txt +++ /dev/null @@ -1,5 +0,0 @@ -# Free domains -gmail.com -googlemail.com -yahoomail.com -*.freemail.org diff --git a/tests/resources/smtp/lists/redirectors.txt b/tests/resources/smtp/lists/redirectors.txt deleted file mode 100644 index 0d31b158..00000000 --- a/tests/resources/smtp/lists/redirectors.txt +++ /dev/null @@ -1,8 +0,0 @@ -bit.ly -redirect.io -redirect.me -redirect.org -redirect.com -redirect.net -t.ly -tinyurl.com diff --git a/tests/src/smtp/inbound/antispam.rs b/tests/src/smtp/inbound/antispam.rs index f0218cad..41058e7f 100644 --- a/tests/src/smtp/inbound/antispam.rs +++ b/tests/src/smtp/inbound/antispam.rs @@ -10,7 +10,7 @@ use std::{ use crate::smtp::session::TestSession; use ahash::AHashMap; use directory::config::ConfigDirectory; -use mail_auth::{dmarc::Policy, DkimResult, DmarcResult, IprevResult, SpfResult}; +use mail_auth::{dmarc::Policy, DkimResult, DmarcResult, IprevResult, SpfResult, MX}; use sieve::runtime::Variable; use smtp::{ config::{scripts::ConfigSieve, ConfigContext, IfBlock}, @@ -57,17 +57,48 @@ type = "memory" [directory."spam".lookup."free-domains"] type = "glob" comment = '#' -values = ["file://%LIST_PATH%/free-domains.txt"] +values = ["gmail.com", "googlemail.com", "yahoomail.com", "*.freemail.org"] [directory."spam".lookup."disposable-domains"] type = "glob" comment = '#' -values = ["file://%LIST_PATH%/disposable-domains.txt"] +values = ["guerrillamail.com", "*.disposable.org"] [directory."spam".lookup."redirectors"] type = "glob" comment = '#' -values = ["file://%LIST_PATH%/redirectors.txt"] +values = ["bit.ly", "redirect.io", "redirect.me", "redirect.org", + "redirect.com", "redirect.net", "t.ly", "tinyurl.com"] + +[directory."spam".lookup."dmarc-allow"] +type = "glob" +comment = '#' +values = ["dmarc-allow.org"] + +[directory."spam".lookup."spf-dkim-allow"] +type = "glob" +comment = '#' +values = ["spf-dkim-allow.org"] + +[directory."spam".lookup."mime-types"] +type = "map" +comment = '#' +values = ["html text/html|BAD", + "pdf application/pdf|NZ", + "txt text/plain|message/disposition-notification|text/rfc822-headers", + "zip AR", + "js BAD|NZ", + "hta BAD|NZ"] + +[directory."spam".lookup."phishing-open"] +type = "glob" +comment = '#' +values = ["*://phishing-open.org", "*://phishing-open.com"] + +[directory."spam".lookup."phishing-tank"] +type = "glob" +comment = '#' +values = ["*://phishing-tank.com", "*://phishing-tank.org"] [resolver] public-suffix = "file://%LIST_PATH%/public-suffix.dat" @@ -98,6 +129,10 @@ async fn antispam() { "mime", "headers", "url", + "dmarc", + "ip", + "helo", + "rbl", ]; let mut core = SMTP::test(); let qr = core.init_test_queue("smtp_antispam_test"); @@ -118,14 +153,11 @@ async fn antispam() { .to_path_buf() .join("resources") .join("config") - .join("sieve") - .to_str() - .unwrap() - .to_string(); + .join("sieve"); + let prelude = fs::read_to_string(base_path.join("prelude.sieve")).unwrap(); for test_name in tests { - config.push_str(&format!( - "{test_name} = \"file://{base_path}/{test_name}.sieve\"\n" - )); + let script = fs::read_to_string(base_path.join(format!("{test_name}.sieve"))).unwrap(); + config.push_str(&format!("{test_name} = '''{prelude}\n{script}\n'''\n")); } // Parse config @@ -137,16 +169,28 @@ async fn antispam() { config.rcpt.relay = IfBlock::new(true); // Add mock DNS entries - core.resolvers.dns.ipv4_add( - "bank.com", - vec!["127.0.0.1".parse().unwrap()], - Instant::now() + Duration::from_secs(100), - ); - core.resolvers.dns.ipv4_add( - "apple.com", - vec!["127.0.0.1".parse().unwrap()], - Instant::now() + Duration::from_secs(100), - ); + for domain in ["bank.com", "apple.com", "youtube.com", "twitter.com"] { + core.resolvers.dns.ipv4_add( + domain, + vec!["127.0.0.1".parse().unwrap()], + Instant::now() + Duration::from_secs(100), + ); + } + for mx in [ + "domain.org", + "domain.co.uk", + "gmail.com", + "custom.disposable.org", + ] { + core.resolvers.dns.mx_add( + mx, + vec![MX { + exchanges: vec!["127.0.0.1".parse().unwrap()], + preference: 10, + }], + Instant::now() + Duration::from_secs(100), + ); + } let core = Arc::new(core); @@ -167,7 +211,7 @@ async fn antispam() { while has_more { let mut message = String::new(); let mut in_params = true; - let mut variables = HashMap::new(); + let mut variables: HashMap> = HashMap::new(); let mut expected_variables = AHashMap::new(); // Build session @@ -193,19 +237,29 @@ async fn antispam() { "spf.result" | "spf_ehlo.result" => { variables.insert( param.to_string(), - SpfResult::from_str(value).as_str().to_string(), + SpfResult::from_str(value).as_str().to_string().into(), ); } "iprev.result" => { variables.insert( param.to_string(), - IprevResult::from_str(value).as_str().to_string(), + IprevResult::from_str(value).as_str().to_string().into(), ); } "dkim.result" | "arc.result" => { variables.insert( param.to_string(), - DkimResult::from_str(value).as_str().to_string(), + DkimResult::from_str(value).as_str().to_string().into(), + ); + } + "dkim.domains" => { + variables.insert( + param.to_string(), + value + .split_ascii_whitespace() + .map(|s| Variable::String(s.to_string())) + .collect::>() + .into(), ); } "envelope_from" => { @@ -218,18 +272,18 @@ async fn antispam() { .push(SessionAddress::new(value.to_string())); } "iprev.ptr" | "dmarc.from" => { - variables.insert(param.to_string(), value.to_string()); + variables.insert(param.to_string(), value.to_string().into()); } "dmarc.result" => { variables.insert( param.to_string(), - DmarcResult::from_str(value).as_str().to_string(), + DmarcResult::from_str(value).as_str().to_string().into(), ); } "dmarc.policy" => { variables.insert( param.to_string(), - Policy::from_str(value).as_str().to_string(), + Policy::from_str(value).as_str().to_string().into(), ); } "expect" => { @@ -249,7 +303,7 @@ async fn antispam() { })); } _ if param.starts_with("param.") | param.starts_with("tls.") => { - variables.insert(param.to_string(), value.to_string()); + variables.insert(param.to_string(), value.to_string().into()); } _ => panic!("Invalid parameter {param:?}"), }