>(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>>
-where
- P: AsRef,
-{
- let file = File::open(filename)?;
- Ok(io::BufReader::new(file).lines())
-}
-
-/// Generate a Sieve script
-/// from a map of destination addresses to a list of their forwarding addresses.
-///
-/// Addresses are sorted according to the order on `OrdEmailAddress`.
-pub fn generate_sieve_script(redirects: AliasMap) -> String {
- let mut script: String =
- "require [\"variables\", \"copy\", \"vnd.stalwart.expressions\", \"envelope\", \"editheader\"];
-
-let \"i\" \"0\";
-while \"i < count(envelope.to)\" {
- let \"redirected\" \"false\";
-"
- .to_string();
- for (redirect, mut destinations) in redirects {
- script += format!(
- // inspired by https://github.com/stalwartlabs/mail-server/issues/916#issuecomment-2474844389
- " if eval \"eq_ignore_case(envelope.to[i], '{}')\" {{
- addheader \"Delivered-To\" \"{}\";
-{}
- deleteheader :index 1 :is \"Delivered-To\" \"{}\";
- let \"redirected\" \"true\";
- }}
-",
- redirect.0,
- redirect.0,
- {
- let mut subscript: String = String::new();
- destinations.sort();
- for destination in destinations.iter() {
- subscript += format!(" redirect :copy \"{}\";\n", destination.0).as_str();
- }
- subscript
- },
- redirect.0
- )
- .as_str();
- }
- script += " if eval \"!redirected\" {
- let \"destination\" \"envelope.to[i]\";
- redirect :copy \"${destination}\";
- }
-";
- script += " let \"i\" \"i+1\";\n";
- script += "}
-discard;";
- script
-}
-
-#[cfg(test)]
-mod tests {
- use super::*;
- use std::str::FromStr;
-
- #[test]
- fn recursion_detection() {
- let result = parse_alias_to_map(vec![AliasFile {
- content: read_lines("testdata/infiniterec.aliases").unwrap(),
- default_domain: FQDN::from_str("example.com").unwrap(),
- }]);
- assert!(result.is_err());
- }
-
- #[test]
- fn basic_parsing() {
- let result = parse_alias_to_map(vec![AliasFile {
- content: read_lines("testdata/simple.aliases").unwrap(),
- default_domain: FQDN::from_str("example.com").unwrap(),
- }])
- .unwrap();
- assert_eq!(result.len(), 4);
-
- for redirects in result.iter() {
- assert_eq!(redirects.1[0].0.to_string(), "me@example.org");
- }
- }
-}
diff --git a/packages/alias-to-sieve/src/main.rs b/packages/alias-to-sieve/src/main.rs
deleted file mode 100644
index 82b2cd3..0000000
--- a/packages/alias-to-sieve/src/main.rs
+++ /dev/null
@@ -1,34 +0,0 @@
-use alias_to_sieve::*;
-use fqdn::FQDN;
-use std::env;
-use std::str::FromStr;
-
-fn main() {
- let args: Vec = env::args().collect();
- if args.len() < 2 || args.len() % 2 == 0 {
- print_help();
- return;
- }
-
- // Collect alias files and their default domains
- let mut alias_files: Vec = Vec::new();
- for i in (1..args.len()).step_by(2) {
- if let Ok(lines) = read_lines(&args[i]) {
- alias_files.push(AliasFile {
- content: lines,
- default_domain: FQDN::from_str(&args[i + 1]).unwrap(),
- });
- }
- }
- println!(
- "{}",
- generate_sieve_script(parse_alias_to_map(alias_files).unwrap())
- );
-}
-
-fn print_help() {
- print!(
- "Reads a virtual alias file and needs a default domain to append to local paths, e.g.
- ./alias_to_sieve example.com.txt example.com"
- );
-}
diff --git a/packages/alias-to-sieve/testdata/simple.aliases b/packages/alias-to-sieve/testdata/simple.aliases
deleted file mode 100644
index 690f620..0000000
--- a/packages/alias-to-sieve/testdata/simple.aliases
+++ /dev/null
@@ -1,4 +0,0 @@
-admin root
-sudo root
-postmaster admin
-root me@example.org
diff --git a/packages/alias-to-sieve/testdata/infiniterec.aliases b/packages/alias_to_sieve/testdata/infiniterec.aliases
similarity index 100%
rename from packages/alias-to-sieve/testdata/infiniterec.aliases
rename to packages/alias_to_sieve/testdata/infiniterec.aliases
diff --git a/packages/flake-module.nix b/packages/flake-module.nix
index 2ffe2c2..0dc6b22 100644
--- a/packages/flake-module.nix
+++ b/packages/flake-module.nix
@@ -20,7 +20,7 @@
packages.alias-to-sieve = let
alias-to-sieve-nix = inputs.crate2nix.tools.${system}.generatedCargoNix {
name = "alias-to-sieve";
- src = ./alias-to-sieve;
+ src = ./alias_to_sieve;
};
in
(pkgs.callPackage alias-to-sieve-nix {}).rootCrate.build;