diff --git a/nixos/modules/mail.nix b/nixos/modules/mail.nix index b8e51f1..71a6559 100644 --- a/nixos/modules/mail.nix +++ b/nixos/modules/mail.nix @@ -98,10 +98,8 @@ in { domains = ["fb04184.mathematik.tu-darmstadt.de" "imap.mathebau.de" "smtp.mathebau.de" "mathebau.de"]; default = true; }; - # HRZ/DFN does spam checking for us and this way we don't need to deal with their possibly broken forwarding setup. - spam-filter.enable = false; - # Reevaluate after DKIM and DMARC deployment + spam.header.is-spam = "Dummyheader"; # disable moving to spam which would conflict with forwarding auth = { # TODO check if HRZ and our own VMs conform to these standards and we can validate them strictly dkim.verify = "relaxed"; diff --git a/packages/alias-to-sieve/src/lib.rs b/packages/alias-to-sieve/src/lib.rs index c0d6842..6622a8a 100644 --- a/packages/alias-to-sieve/src/lib.rs +++ b/packages/alias-to-sieve/src/lib.rs @@ -13,58 +13,28 @@ pub struct AliasFile { } #[derive(PartialEq, Eq, Clone, Debug)] -pub struct AliasEmailAddress(EmailAddress); +pub struct OrdEmailAddress(EmailAddress); -impl AliasEmailAddress { - /// Create an `AliasEmailAddress` from some alias entry. - /// Return parameter for complete mail addresses and append the default domain for local parts. - pub fn new( - alias_entry: &str, - default_domain: &FQDN, - ) -> Result> { - let mut addr = alias_entry.trim().to_string(); - addr = addr.replace(',', ""); - - // The domain already fails on instantiation of the FQDN type if it contains an apostrophe. - if addr.contains('\'') { - return Err(format!( - "Mailaddress {addr} contains an apostrophe which breaks the script generation." - ) - .into()); - } - - if addr.contains('@') { - return Ok(AliasEmailAddress( - EmailAddress::parse(&addr, None).ok_or::>( - String::from("Mailaddress {addr} not parsable.").into(), - )?, - )); - } - let unsortable_mail = EmailAddress::new(&addr, &default_domain.to_string(), None)?; - Ok(AliasEmailAddress(unsortable_mail)) - } -} - -impl PartialOrd for AliasEmailAddress { +impl PartialOrd for OrdEmailAddress { fn partial_cmp(&self, other: &Self) -> Option { Some(self.0.to_string().cmp(&other.0.to_string())) } } -impl Ord for AliasEmailAddress { +impl Ord for OrdEmailAddress { fn cmp(&self, other: &Self) -> Ordering { self.0.to_string().cmp(&other.0.to_string()) } } -pub type AliasMap = BTreeMap>; +pub type AliasMap = BTreeMap>; /// Read a virtual alias file /// and convert it to a map of destination addresses to a list of their final forwarding addresses. pub fn parse_alias_to_map(alias_files: Vec) -> Result> { // File must exist in the current path let mut redirect_map: AliasMap = AliasMap::new(); - let mut destinations: Vec = Vec::new(); + let mut destinations: Vec = Vec::new(); // Extract all pairs (destination to redirect addresses) from the alias files for alias_file in alias_files { @@ -78,23 +48,25 @@ pub fn parse_alias_to_map(alias_files: Vec) -> Result = line + if destination.contains('\'') { + return Err(format!("Mailaddress {destination} contains an apostrophe which is not allowed by HRZ.") + .into()); + } + + let redirects: Vec = line .split_at(line.find(char::is_whitespace).unwrap_or(0)) .1 .split(' ') .filter(|address| !address.trim().to_string().replace(',', "").is_empty()) - .map(|addr| AliasEmailAddress::new(addr, &alias_file.default_domain)) + .map(|addr| to_mailaddress(addr, &alias_file.default_domain)) .collect::, _>>()?; if redirects.is_empty() { continue; } - destinations.push(AliasEmailAddress::new( - destination, - &alias_file.default_domain, - )?); + destinations.push(to_mailaddress(destination, &alias_file.default_domain)?); redirect_map.insert( - AliasEmailAddress::new(destination, &alias_file.default_domain)?, + to_mailaddress(destination, &alias_file.default_domain)?, redirects, ); } @@ -133,6 +105,24 @@ pub fn parse_alias_to_map(alias_files: Vec) -> Result Result> { + let mut addr = alias_entry.trim().to_string(); + addr = addr.replace(',', ""); + if addr.contains('@') { + return Ok(OrdEmailAddress( + EmailAddress::parse(&addr, None) + .ok_or::>(String::from("Mailaddress {addr} not parsable.").into())?, + )); + } + let unsortable_mail = EmailAddress::new(&addr, &default_domain.to_string(), None)?; + Ok(OrdEmailAddress(unsortable_mail)) +} + // The output is wrapped in a Result to allow matching on errors. // Returns an Iterator to the Reader of the lines of the file. pub fn read_lines

(filename: P) -> io::Result>> @@ -206,17 +196,9 @@ mod tests { } #[test] - fn apostrophe_destination_detection() { + fn apostrophe_detection() { let result = parse_alias_to_map(vec![AliasFile { - content: read_lines("testdata/apostrophe_destination.aliases").unwrap(), - default_domain: FQDN::from_str("example.com").unwrap(), - }]); - assert!(result.is_err()); - } - #[test] - fn apostrophe_redirect_detection() { - let result = parse_alias_to_map(vec![AliasFile { - content: read_lines("testdata/apostrophe_redirect.aliases").unwrap(), + content: read_lines("testdata/apostrophe.aliases").unwrap(), default_domain: FQDN::from_str("example.com").unwrap(), }]); assert!(result.is_err()); diff --git a/packages/alias-to-sieve/testdata/apostrophe.aliases b/packages/alias-to-sieve/testdata/apostrophe.aliases new file mode 100644 index 0000000..37a6f91 --- /dev/null +++ b/packages/alias-to-sieve/testdata/apostrophe.aliases @@ -0,0 +1,2 @@ +# Apostrophes are not allowed by HRZ +'orga me@example.com diff --git a/packages/alias-to-sieve/testdata/apostrophe_destination.aliases b/packages/alias-to-sieve/testdata/apostrophe_destination.aliases deleted file mode 100644 index 5cc3e26..0000000 --- a/packages/alias-to-sieve/testdata/apostrophe_destination.aliases +++ /dev/null @@ -1,2 +0,0 @@ -# Apostrophes are not allowed -'orga me@example.com diff --git a/packages/alias-to-sieve/testdata/apostrophe_redirect.aliases b/packages/alias-to-sieve/testdata/apostrophe_redirect.aliases deleted file mode 100644 index d6b499f..0000000 --- a/packages/alias-to-sieve/testdata/apostrophe_redirect.aliases +++ /dev/null @@ -1,2 +0,0 @@ -# Apostrophes are not allowed -orga me@e'xample.com