Add package 'mailnix' for assisting in generation of dovecot/postfix files
This commit is contained in:
parent
ce2784e17d
commit
955a0ec8ba
9 changed files with 1136 additions and 2 deletions
16
flake.lock
generated
16
flake.lock
generated
|
|
@ -1,5 +1,20 @@
|
||||||
{
|
{
|
||||||
"nodes": {
|
"nodes": {
|
||||||
|
"crane": {
|
||||||
|
"locked": {
|
||||||
|
"lastModified": 1739936662,
|
||||||
|
"narHash": "sha256-x4syUjNUuRblR07nDPeLDP7DpphaBVbUaSoeZkFbGSk=",
|
||||||
|
"owner": "ipetkov",
|
||||||
|
"repo": "crane",
|
||||||
|
"rev": "19de14aaeb869287647d9461cbd389187d8ecdb7",
|
||||||
|
"type": "github"
|
||||||
|
},
|
||||||
|
"original": {
|
||||||
|
"owner": "ipetkov",
|
||||||
|
"repo": "crane",
|
||||||
|
"type": "github"
|
||||||
|
}
|
||||||
|
},
|
||||||
"flake-compat": {
|
"flake-compat": {
|
||||||
"flake": false,
|
"flake": false,
|
||||||
"locked": {
|
"locked": {
|
||||||
|
|
@ -97,6 +112,7 @@
|
||||||
},
|
},
|
||||||
"root": {
|
"root": {
|
||||||
"inputs": {
|
"inputs": {
|
||||||
|
"crane": "crane",
|
||||||
"flake-parts": "flake-parts",
|
"flake-parts": "flake-parts",
|
||||||
"git-hooks-nix": "git-hooks-nix",
|
"git-hooks-nix": "git-hooks-nix",
|
||||||
"nixpkgs": "nixpkgs",
|
"nixpkgs": "nixpkgs",
|
||||||
|
|
|
||||||
21
flake.nix
21
flake.nix
|
|
@ -7,6 +7,7 @@
|
||||||
flake-parts.inputs.nixpkgs-lib.follows = "nixpkgs";
|
flake-parts.inputs.nixpkgs-lib.follows = "nixpkgs";
|
||||||
treefmt-nix.url = "github:numtide/treefmt-nix";
|
treefmt-nix.url = "github:numtide/treefmt-nix";
|
||||||
treefmt-nix.inputs.nixpkgs.follows = "nixpkgs";
|
treefmt-nix.inputs.nixpkgs.follows = "nixpkgs";
|
||||||
|
crane.url = "github:ipetkov/crane";
|
||||||
git-hooks-nix.url = "github:cachix/git-hooks.nix";
|
git-hooks-nix.url = "github:cachix/git-hooks.nix";
|
||||||
git-hooks-nix.inputs.nixpkgs.follows = "nixpkgs";
|
git-hooks-nix.inputs.nixpkgs.follows = "nixpkgs";
|
||||||
};
|
};
|
||||||
|
|
@ -23,6 +24,7 @@
|
||||||
"aarch64-linux"
|
"aarch64-linux"
|
||||||
];
|
];
|
||||||
imports = [
|
imports = [
|
||||||
|
flake-parts.flakeModules.easyOverlay
|
||||||
inputs.treefmt-nix.flakeModule
|
inputs.treefmt-nix.flakeModule
|
||||||
inputs.git-hooks-nix.flakeModule
|
inputs.git-hooks-nix.flakeModule
|
||||||
];
|
];
|
||||||
|
|
@ -34,7 +36,10 @@
|
||||||
pkgs,
|
pkgs,
|
||||||
system,
|
system,
|
||||||
...
|
...
|
||||||
}: {
|
}: let
|
||||||
|
craneLib = inputs.crane.mkLib pkgs;
|
||||||
|
pkgs = nixpkgs.legacyPackages.${system}.extend self.overlays.default;
|
||||||
|
in {
|
||||||
checks = let
|
checks = let
|
||||||
tests = ["internal" "basic" "aliases" "rspamd"];
|
tests = ["internal" "basic" "aliases" "rspamd"];
|
||||||
genTest = testName: {
|
genTest = testName: {
|
||||||
|
|
@ -44,9 +49,20 @@
|
||||||
in
|
in
|
||||||
pkgs.lib.listToAttrs (map genTest tests);
|
pkgs.lib.listToAttrs (map genTest tests);
|
||||||
|
|
||||||
devShells.default = pkgs.mkShell {
|
packages = rec {
|
||||||
|
default = mailnix;
|
||||||
|
mailnix = craneLib.buildPackage {
|
||||||
|
src = craneLib.cleanCargoSource ./pkgs/mailnix;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
overlayAttrs = {
|
||||||
|
mailnix = config.packages.mailnix;
|
||||||
|
};
|
||||||
|
|
||||||
|
devShells.default = craneLib.devShell {
|
||||||
packages = with pkgs; [
|
packages = with pkgs; [
|
||||||
self'.formatter.outPath # Add all formatters to environment
|
self'.formatter.outPath # Add all formatters to environment
|
||||||
|
mailnix
|
||||||
];
|
];
|
||||||
shellHook = ''
|
shellHook = ''
|
||||||
${config.pre-commit.installationScript}
|
${config.pre-commit.installationScript}
|
||||||
|
|
@ -61,6 +77,7 @@
|
||||||
programs = {
|
programs = {
|
||||||
actionlint.enable = true;
|
actionlint.enable = true;
|
||||||
alejandra.enable = true;
|
alejandra.enable = true;
|
||||||
|
rustfmt.enable = true;
|
||||||
};
|
};
|
||||||
settings.global.excludes = [
|
settings.global.excludes = [
|
||||||
".envrc"
|
".envrc"
|
||||||
|
|
|
||||||
749
pkgs/mailnix/Cargo.lock
generated
Normal file
749
pkgs/mailnix/Cargo.lock
generated
Normal file
|
|
@ -0,0 +1,749 @@
|
||||||
|
# This file is automatically @generated by Cargo.
|
||||||
|
# It is not intended for manual editing.
|
||||||
|
version = 3
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "aho-corasick"
|
||||||
|
version = "1.1.3"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "8e60d3430d3a69478ad0993f19238d2df97c507009a52b3c10addcd7f6bcb916"
|
||||||
|
dependencies = [
|
||||||
|
"memchr",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "android-tzdata"
|
||||||
|
version = "0.1.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "e999941b234f3131b00bc13c22d06e8c5ff726d1b6318ac7eb276997bbb4fef0"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "android_system_properties"
|
||||||
|
version = "0.1.5"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "819e7219dbd41043ac279b19830f2efc897156490d7fd6ea916720117ee66311"
|
||||||
|
dependencies = [
|
||||||
|
"libc",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "anstream"
|
||||||
|
version = "0.6.18"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "8acc5369981196006228e28809f761875c0327210a891e941f4c683b3a99529b"
|
||||||
|
dependencies = [
|
||||||
|
"anstyle",
|
||||||
|
"anstyle-parse",
|
||||||
|
"anstyle-query",
|
||||||
|
"anstyle-wincon",
|
||||||
|
"colorchoice",
|
||||||
|
"is_terminal_polyfill",
|
||||||
|
"utf8parse",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "anstyle"
|
||||||
|
version = "1.0.10"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "55cc3b69f167a1ef2e161439aa98aed94e6028e5f9a59be9a6ffb47aef1651f9"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "anstyle-parse"
|
||||||
|
version = "0.2.6"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "3b2d16507662817a6a20a9ea92df6652ee4f94f914589377d69f3b21bc5798a9"
|
||||||
|
dependencies = [
|
||||||
|
"utf8parse",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "anstyle-query"
|
||||||
|
version = "1.1.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "79947af37f4177cfead1110013d678905c37501914fba0efea834c3fe9a8d60c"
|
||||||
|
dependencies = [
|
||||||
|
"windows-sys",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "anstyle-wincon"
|
||||||
|
version = "3.0.7"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "ca3534e77181a9cc07539ad51f2141fe32f6c3ffd4df76db8ad92346b003ae4e"
|
||||||
|
dependencies = [
|
||||||
|
"anstyle",
|
||||||
|
"once_cell",
|
||||||
|
"windows-sys",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "anyhow"
|
||||||
|
version = "1.0.96"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "6b964d184e89d9b6b67dd2715bc8e74cf3107fb2b529990c90cf517326150bf4"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "autocfg"
|
||||||
|
version = "1.4.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "ace50bade8e6234aa140d9a2f552bbee1db4d353f69b8217bc503490fc1a9f26"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "base64"
|
||||||
|
version = "0.22.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "72b3254f16251a8381aa12e40e3c4d2f0199f8c6508fbecb9d91f575e0fbb8c6"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "bumpalo"
|
||||||
|
version = "3.17.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "1628fb46dfa0b37568d12e5edd512553eccf6a22a78e8bde00bb4aed84d5bdbf"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "cc"
|
||||||
|
version = "1.2.15"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "c736e259eea577f443d5c86c304f9f4ae0295c43f3ba05c21f1d66b5f06001af"
|
||||||
|
dependencies = [
|
||||||
|
"shlex",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "cfg-if"
|
||||||
|
version = "1.0.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "chrono"
|
||||||
|
version = "0.4.39"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "7e36cc9d416881d2e24f9a963be5fb1cd90966419ac844274161d10488b3e825"
|
||||||
|
dependencies = [
|
||||||
|
"android-tzdata",
|
||||||
|
"iana-time-zone",
|
||||||
|
"num-traits",
|
||||||
|
"serde",
|
||||||
|
"windows-targets",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "clap"
|
||||||
|
version = "4.5.30"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "92b7b18d71fad5313a1e320fa9897994228ce274b60faa4d694fe0ea89cd9e6d"
|
||||||
|
dependencies = [
|
||||||
|
"clap_builder",
|
||||||
|
"clap_derive",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "clap_builder"
|
||||||
|
version = "4.5.30"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "a35db2071778a7344791a4fb4f95308b5673d219dee3ae348b86642574ecc90c"
|
||||||
|
dependencies = [
|
||||||
|
"anstream",
|
||||||
|
"anstyle",
|
||||||
|
"clap_lex",
|
||||||
|
"strsim",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "clap_derive"
|
||||||
|
version = "4.5.28"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "bf4ced95c6f4a675af3da73304b9ac4ed991640c36374e4b46795c49e17cf1ed"
|
||||||
|
dependencies = [
|
||||||
|
"heck",
|
||||||
|
"proc-macro2",
|
||||||
|
"quote",
|
||||||
|
"syn",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "clap_lex"
|
||||||
|
version = "0.7.4"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "f46ad14479a25103f283c0f10005961cf086d8dc42205bb44c46ac563475dca6"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "colorchoice"
|
||||||
|
version = "1.0.3"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "5b63caa9aa9397e2d9480a9b13673856c78d8ac123288526c37d7839f2a86990"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "core-foundation-sys"
|
||||||
|
version = "0.8.7"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "773648b94d0e5d620f64f280777445740e61fe701025087ec8b57f45c791888b"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "darling"
|
||||||
|
version = "0.20.10"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "6f63b86c8a8826a49b8c21f08a2d07338eec8d900540f8630dc76284be802989"
|
||||||
|
dependencies = [
|
||||||
|
"darling_core",
|
||||||
|
"darling_macro",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "darling_core"
|
||||||
|
version = "0.20.10"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "95133861a8032aaea082871032f5815eb9e98cef03fa916ab4500513994df9e5"
|
||||||
|
dependencies = [
|
||||||
|
"fnv",
|
||||||
|
"ident_case",
|
||||||
|
"proc-macro2",
|
||||||
|
"quote",
|
||||||
|
"strsim",
|
||||||
|
"syn",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "darling_macro"
|
||||||
|
version = "0.20.10"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "d336a2a514f6ccccaa3e09b02d41d35330c07ddf03a62165fcec10bb561c7806"
|
||||||
|
dependencies = [
|
||||||
|
"darling_core",
|
||||||
|
"quote",
|
||||||
|
"syn",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "deranged"
|
||||||
|
version = "0.3.11"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "b42b6fa04a440b495c8b04d0e71b707c585f83cb9cb28cf8cd0d976c315e31b4"
|
||||||
|
dependencies = [
|
||||||
|
"powerfmt",
|
||||||
|
"serde",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "equivalent"
|
||||||
|
version = "1.0.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "877a4ace8713b0bcf2a4e7eec82529c029f1d0619886d18145fea96c3ffe5c0f"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "fnv"
|
||||||
|
version = "1.0.7"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "hashbrown"
|
||||||
|
version = "0.12.3"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "8a9ee70c43aaf417c914396645a0fa852624801b24ebb7ae78fe8272889ac888"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "hashbrown"
|
||||||
|
version = "0.15.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "bf151400ff0baff5465007dd2f3e717f3fe502074ca563069ce3a6629d07b289"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "heck"
|
||||||
|
version = "0.5.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "hex"
|
||||||
|
version = "0.4.3"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "iana-time-zone"
|
||||||
|
version = "0.1.61"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "235e081f3925a06703c2d0117ea8b91f042756fd6e7a6e5d901e8ca1a996b220"
|
||||||
|
dependencies = [
|
||||||
|
"android_system_properties",
|
||||||
|
"core-foundation-sys",
|
||||||
|
"iana-time-zone-haiku",
|
||||||
|
"js-sys",
|
||||||
|
"wasm-bindgen",
|
||||||
|
"windows-core",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "iana-time-zone-haiku"
|
||||||
|
version = "0.1.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "f31827a206f56af32e590ba56d5d2d085f558508192593743f16b2306495269f"
|
||||||
|
dependencies = [
|
||||||
|
"cc",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "ident_case"
|
||||||
|
version = "1.0.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "b9e0384b61958566e926dc50660321d12159025e767c18e043daf26b70104c39"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "indexmap"
|
||||||
|
version = "1.9.3"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "bd070e393353796e801d209ad339e89596eb4c8d430d18ede6a1cced8fafbd99"
|
||||||
|
dependencies = [
|
||||||
|
"autocfg",
|
||||||
|
"hashbrown 0.12.3",
|
||||||
|
"serde",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "indexmap"
|
||||||
|
version = "2.7.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "8c9c992b02b5b4c94ea26e32fe5bccb7aa7d9f390ab5c1221ff895bc7ea8b652"
|
||||||
|
dependencies = [
|
||||||
|
"equivalent",
|
||||||
|
"hashbrown 0.15.2",
|
||||||
|
"serde",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "is_terminal_polyfill"
|
||||||
|
version = "1.70.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "7943c866cc5cd64cbc25b2e01621d07fa8eb2a1a23160ee81ce38704e97b8ecf"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "itoa"
|
||||||
|
version = "1.0.14"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "d75a2a4b1b190afb6f5425f10f6a8f959d2ea0b9c2b1d79553551850539e4674"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "js-sys"
|
||||||
|
version = "0.3.77"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "1cfaf33c695fc6e08064efbc1f72ec937429614f25eef83af942d0e227c3a28f"
|
||||||
|
dependencies = [
|
||||||
|
"once_cell",
|
||||||
|
"wasm-bindgen",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "libc"
|
||||||
|
version = "0.2.169"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "b5aba8db14291edd000dfcc4d620c7ebfb122c613afb886ca8803fa4e128a20a"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "libyml"
|
||||||
|
version = "0.0.5"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "3302702afa434ffa30847a83305f0a69d6abd74293b6554c18ec85c7ef30c980"
|
||||||
|
dependencies = [
|
||||||
|
"anyhow",
|
||||||
|
"version_check",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "log"
|
||||||
|
version = "0.4.26"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "30bde2b3dc3671ae49d8e2e9f044c7c005836e7a023ee57cffa25ab82764bb9e"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "mailnix"
|
||||||
|
version = "0.1.0"
|
||||||
|
dependencies = [
|
||||||
|
"clap",
|
||||||
|
"regex",
|
||||||
|
"serde",
|
||||||
|
"serde_with",
|
||||||
|
"serde_yml",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "memchr"
|
||||||
|
version = "2.7.4"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "78ca9ab1a0babb1e7d5695e3530886289c18cf2f87ec19a575a0abdce112e3a3"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "num-conv"
|
||||||
|
version = "0.1.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "51d515d32fb182ee37cda2ccdcb92950d6a3c2893aa280e540671c2cd0f3b1d9"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "num-traits"
|
||||||
|
version = "0.2.19"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "071dfc062690e90b734c0b2273ce72ad0ffa95f0c74596bc250dcfd960262841"
|
||||||
|
dependencies = [
|
||||||
|
"autocfg",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "once_cell"
|
||||||
|
version = "1.20.3"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "945462a4b81e43c4e3ba96bd7b49d834c6f61198356aa858733bc4acf3cbe62e"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "powerfmt"
|
||||||
|
version = "0.2.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "439ee305def115ba05938db6eb1644ff94165c5ab5e9420d1c1bcedbba909391"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "proc-macro2"
|
||||||
|
version = "1.0.93"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "60946a68e5f9d28b0dc1c21bb8a97ee7d018a8b322fa57838ba31cc878e22d99"
|
||||||
|
dependencies = [
|
||||||
|
"unicode-ident",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "quote"
|
||||||
|
version = "1.0.38"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "0e4dccaaaf89514f546c693ddc140f729f958c247918a13380cccc6078391acc"
|
||||||
|
dependencies = [
|
||||||
|
"proc-macro2",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "regex"
|
||||||
|
version = "1.11.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "b544ef1b4eac5dc2db33ea63606ae9ffcfac26c1416a2806ae0bf5f56b201191"
|
||||||
|
dependencies = [
|
||||||
|
"aho-corasick",
|
||||||
|
"memchr",
|
||||||
|
"regex-automata",
|
||||||
|
"regex-syntax",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "regex-automata"
|
||||||
|
version = "0.4.9"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "809e8dc61f6de73b46c85f4c96486310fe304c434cfa43669d7b40f711150908"
|
||||||
|
dependencies = [
|
||||||
|
"aho-corasick",
|
||||||
|
"memchr",
|
||||||
|
"regex-syntax",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "regex-syntax"
|
||||||
|
version = "0.8.5"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "2b15c43186be67a4fd63bee50d0303afffcef381492ebe2c5d87f324e1b8815c"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "rustversion"
|
||||||
|
version = "1.0.19"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "f7c45b9784283f1b2e7fb61b42047c2fd678ef0960d4f6f1eba131594cc369d4"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "ryu"
|
||||||
|
version = "1.0.19"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "6ea1a2d0a644769cc99faa24c3ad26b379b786fe7c36fd3c546254801650e6dd"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "serde"
|
||||||
|
version = "1.0.218"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "e8dfc9d19bdbf6d17e22319da49161d5d0108e4188e8b680aef6299eed22df60"
|
||||||
|
dependencies = [
|
||||||
|
"serde_derive",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "serde_derive"
|
||||||
|
version = "1.0.218"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "f09503e191f4e797cb8aac08e9a4a4695c5edf6a2e70e376d961ddd5c969f82b"
|
||||||
|
dependencies = [
|
||||||
|
"proc-macro2",
|
||||||
|
"quote",
|
||||||
|
"syn",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "serde_json"
|
||||||
|
version = "1.0.139"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "44f86c3acccc9c65b153fe1b85a3be07fe5515274ec9f0653b4a0875731c72a6"
|
||||||
|
dependencies = [
|
||||||
|
"itoa",
|
||||||
|
"memchr",
|
||||||
|
"ryu",
|
||||||
|
"serde",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "serde_with"
|
||||||
|
version = "3.12.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "d6b6f7f2fcb69f747921f79f3926bd1e203fce4fef62c268dd3abfb6d86029aa"
|
||||||
|
dependencies = [
|
||||||
|
"base64",
|
||||||
|
"chrono",
|
||||||
|
"hex",
|
||||||
|
"indexmap 1.9.3",
|
||||||
|
"indexmap 2.7.1",
|
||||||
|
"serde",
|
||||||
|
"serde_derive",
|
||||||
|
"serde_json",
|
||||||
|
"serde_with_macros",
|
||||||
|
"time",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "serde_with_macros"
|
||||||
|
version = "3.12.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "8d00caa5193a3c8362ac2b73be6b9e768aa5a4b2f721d8f4b339600c3cb51f8e"
|
||||||
|
dependencies = [
|
||||||
|
"darling",
|
||||||
|
"proc-macro2",
|
||||||
|
"quote",
|
||||||
|
"syn",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "serde_yml"
|
||||||
|
version = "0.0.12"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "59e2dd588bf1597a252c3b920e0143eb99b0f76e4e082f4c92ce34fbc9e71ddd"
|
||||||
|
dependencies = [
|
||||||
|
"indexmap 2.7.1",
|
||||||
|
"itoa",
|
||||||
|
"libyml",
|
||||||
|
"memchr",
|
||||||
|
"ryu",
|
||||||
|
"serde",
|
||||||
|
"version_check",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "shlex"
|
||||||
|
version = "1.3.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "strsim"
|
||||||
|
version = "0.11.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "7da8b5736845d9f2fcb837ea5d9e2628564b3b043a70948a3f0b778838c5fb4f"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "syn"
|
||||||
|
version = "2.0.98"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "36147f1a48ae0ec2b5b3bc5b537d267457555a10dc06f3dbc8cb11ba3006d3b1"
|
||||||
|
dependencies = [
|
||||||
|
"proc-macro2",
|
||||||
|
"quote",
|
||||||
|
"unicode-ident",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "time"
|
||||||
|
version = "0.3.37"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "35e7868883861bd0e56d9ac6efcaaca0d6d5d82a2a7ec8209ff492c07cf37b21"
|
||||||
|
dependencies = [
|
||||||
|
"deranged",
|
||||||
|
"itoa",
|
||||||
|
"num-conv",
|
||||||
|
"powerfmt",
|
||||||
|
"serde",
|
||||||
|
"time-core",
|
||||||
|
"time-macros",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "time-core"
|
||||||
|
version = "0.1.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "ef927ca75afb808a4d64dd374f00a2adf8d0fcff8e7b184af886c3c87ec4a3f3"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "time-macros"
|
||||||
|
version = "0.2.19"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "2834e6017e3e5e4b9834939793b282bc03b37a3336245fa820e35e233e2a85de"
|
||||||
|
dependencies = [
|
||||||
|
"num-conv",
|
||||||
|
"time-core",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "unicode-ident"
|
||||||
|
version = "1.0.17"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "00e2473a93778eb0bad35909dff6a10d28e63f792f16ed15e404fca9d5eeedbe"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "utf8parse"
|
||||||
|
version = "0.2.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "06abde3611657adf66d383f00b093d7faecc7fa57071cce2578660c9f1010821"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "version_check"
|
||||||
|
version = "0.9.5"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "0b928f33d975fc6ad9f86c8f283853ad26bdd5b10b7f1542aa2fa15e2289105a"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "wasm-bindgen"
|
||||||
|
version = "0.2.100"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "1edc8929d7499fc4e8f0be2262a241556cfc54a0bea223790e71446f2aab1ef5"
|
||||||
|
dependencies = [
|
||||||
|
"cfg-if",
|
||||||
|
"once_cell",
|
||||||
|
"rustversion",
|
||||||
|
"wasm-bindgen-macro",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "wasm-bindgen-backend"
|
||||||
|
version = "0.2.100"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "2f0a0651a5c2bc21487bde11ee802ccaf4c51935d0d3d42a6101f98161700bc6"
|
||||||
|
dependencies = [
|
||||||
|
"bumpalo",
|
||||||
|
"log",
|
||||||
|
"proc-macro2",
|
||||||
|
"quote",
|
||||||
|
"syn",
|
||||||
|
"wasm-bindgen-shared",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "wasm-bindgen-macro"
|
||||||
|
version = "0.2.100"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "7fe63fc6d09ed3792bd0897b314f53de8e16568c2b3f7982f468c0bf9bd0b407"
|
||||||
|
dependencies = [
|
||||||
|
"quote",
|
||||||
|
"wasm-bindgen-macro-support",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "wasm-bindgen-macro-support"
|
||||||
|
version = "0.2.100"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "8ae87ea40c9f689fc23f209965b6fb8a99ad69aeeb0231408be24920604395de"
|
||||||
|
dependencies = [
|
||||||
|
"proc-macro2",
|
||||||
|
"quote",
|
||||||
|
"syn",
|
||||||
|
"wasm-bindgen-backend",
|
||||||
|
"wasm-bindgen-shared",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "wasm-bindgen-shared"
|
||||||
|
version = "0.2.100"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "1a05d73b933a847d6cccdda8f838a22ff101ad9bf93e33684f39c1f5f0eece3d"
|
||||||
|
dependencies = [
|
||||||
|
"unicode-ident",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "windows-core"
|
||||||
|
version = "0.52.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "33ab640c8d7e35bf8ba19b884ba838ceb4fba93a4e8c65a9059d08afcfc683d9"
|
||||||
|
dependencies = [
|
||||||
|
"windows-targets",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "windows-sys"
|
||||||
|
version = "0.59.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "1e38bc4d79ed67fd075bcc251a1c39b32a1776bbe92e5bef1f0bf1f8c531853b"
|
||||||
|
dependencies = [
|
||||||
|
"windows-targets",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "windows-targets"
|
||||||
|
version = "0.52.6"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "9b724f72796e036ab90c1021d4780d4d3d648aca59e491e6b98e725b84e99973"
|
||||||
|
dependencies = [
|
||||||
|
"windows_aarch64_gnullvm",
|
||||||
|
"windows_aarch64_msvc",
|
||||||
|
"windows_i686_gnu",
|
||||||
|
"windows_i686_gnullvm",
|
||||||
|
"windows_i686_msvc",
|
||||||
|
"windows_x86_64_gnu",
|
||||||
|
"windows_x86_64_gnullvm",
|
||||||
|
"windows_x86_64_msvc",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "windows_aarch64_gnullvm"
|
||||||
|
version = "0.52.6"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "windows_aarch64_msvc"
|
||||||
|
version = "0.52.6"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "windows_i686_gnu"
|
||||||
|
version = "0.52.6"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "8e9b5ad5ab802e97eb8e295ac6720e509ee4c243f69d781394014ebfe8bbfa0b"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "windows_i686_gnullvm"
|
||||||
|
version = "0.52.6"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "windows_i686_msvc"
|
||||||
|
version = "0.52.6"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "windows_x86_64_gnu"
|
||||||
|
version = "0.52.6"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "windows_x86_64_gnullvm"
|
||||||
|
version = "0.52.6"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "windows_x86_64_msvc"
|
||||||
|
version = "0.52.6"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec"
|
||||||
11
pkgs/mailnix/Cargo.toml
Normal file
11
pkgs/mailnix/Cargo.toml
Normal file
|
|
@ -0,0 +1,11 @@
|
||||||
|
[package]
|
||||||
|
name = "mailnix"
|
||||||
|
version = "0.1.0"
|
||||||
|
edition = "2021"
|
||||||
|
|
||||||
|
[dependencies]
|
||||||
|
clap = { version = "4.5.28", features = ["derive"] }
|
||||||
|
regex = "1.11.1"
|
||||||
|
serde = { version = "1.0.217", features = ["derive", "rc"] }
|
||||||
|
serde_with = "3.12.0"
|
||||||
|
serde_yml = "0.0.12"
|
||||||
22
pkgs/mailnix/src/cli.rs
Normal file
22
pkgs/mailnix/src/cli.rs
Normal file
|
|
@ -0,0 +1,22 @@
|
||||||
|
use clap::{Parser, Subcommand};
|
||||||
|
|
||||||
|
#[derive(Subcommand, Debug)]
|
||||||
|
pub enum Commands {
|
||||||
|
Check,
|
||||||
|
GenerateUserdb,
|
||||||
|
GenerateStaticPassdb,
|
||||||
|
UpdateDynamicPassdb { path: String },
|
||||||
|
GenerateAliases,
|
||||||
|
GenerateDeniedRecipients,
|
||||||
|
GenerateDomains,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Parser, Debug)]
|
||||||
|
#[command(author, version, about)]
|
||||||
|
pub struct Cli {
|
||||||
|
pub config_path: String,
|
||||||
|
pub additional_config_path: Option<String>,
|
||||||
|
|
||||||
|
#[command(subcommand)]
|
||||||
|
pub command: Commands,
|
||||||
|
}
|
||||||
199
pkgs/mailnix/src/config.rs
Normal file
199
pkgs/mailnix/src/config.rs
Normal file
|
|
@ -0,0 +1,199 @@
|
||||||
|
use serde::Deserialize;
|
||||||
|
use serde_with::formats::PreferMany;
|
||||||
|
use serde_with::{serde_as, MapPreventDuplicates, OneOrMany};
|
||||||
|
|
||||||
|
use regex::Regex;
|
||||||
|
use std::collections::{HashMap, HashSet};
|
||||||
|
use std::error::Error;
|
||||||
|
use std::fs::{self, File};
|
||||||
|
use std::io::BufReader;
|
||||||
|
use std::path::Path;
|
||||||
|
|
||||||
|
#[serde_as]
|
||||||
|
#[derive(Debug, Deserialize)]
|
||||||
|
#[serde(deny_unknown_fields)]
|
||||||
|
pub struct Config {
|
||||||
|
#[serde(default)]
|
||||||
|
pub domains: Vec<String>,
|
||||||
|
|
||||||
|
#[serde(default)]
|
||||||
|
#[serde_as(deserialize_as = "MapPreventDuplicates<_, _>")]
|
||||||
|
pub accounts: HashMap<String, AccountConfig>,
|
||||||
|
|
||||||
|
#[serde(default)]
|
||||||
|
#[serde_as(deserialize_as = "MapPreventDuplicates<_, OneOrMany<_, PreferMany>>")]
|
||||||
|
pub aliases: HashMap<String, Vec<String>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
fn default_reject_message() -> String {
|
||||||
|
"This account cannot receive emails.".to_string()
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Debug, Deserialize)]
|
||||||
|
pub struct AccountConfig {
|
||||||
|
#[serde(rename(deserialize = "hashedPassword"))]
|
||||||
|
#[serde(skip)]
|
||||||
|
pub hashed_password: String,
|
||||||
|
|
||||||
|
#[serde(rename(deserialize = "hashedPassword"))]
|
||||||
|
hashed_password_: Option<String>,
|
||||||
|
|
||||||
|
#[serde(rename(deserialize = "hashedPasswordFile"))]
|
||||||
|
hashed_password_file_: Option<String>,
|
||||||
|
|
||||||
|
#[serde(default, rename(deserialize = "isSystemUser"))]
|
||||||
|
pub is_system_user: bool,
|
||||||
|
|
||||||
|
#[serde(
|
||||||
|
default = "default_reject_message",
|
||||||
|
rename(deserialize = "rejectMessage")
|
||||||
|
)]
|
||||||
|
pub reject_message: String,
|
||||||
|
}
|
||||||
|
|
||||||
|
fn sanitize_vec(vec: &mut [String]) -> Result<(), Box<dyn Error>> {
|
||||||
|
let mut seen = HashSet::new();
|
||||||
|
for item in vec.iter_mut() {
|
||||||
|
*item = item.to_lowercase();
|
||||||
|
if !seen.insert(item.clone()) {
|
||||||
|
return Err(format!("Duplicate entry ({item}) detected, aborting...").into());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn sanitize_map_keys<T: Clone>(map: &mut HashMap<String, T>) -> Result<(), Box<dyn Error>> {
|
||||||
|
let mut new_map = HashMap::<String, T>::new();
|
||||||
|
let mut seen = HashSet::new();
|
||||||
|
for (k, v) in map.iter() {
|
||||||
|
let new_k = k.to_lowercase();
|
||||||
|
new_map.insert(new_k.clone(), v.clone());
|
||||||
|
if !seen.insert(new_k.clone()) {
|
||||||
|
return Err(format!("Duplicate entry ({new_k}) detected, aborting...").into());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
*map = new_map;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Config {
|
||||||
|
pub fn load<P: AsRef<Path>>(path: P) -> Result<Config, Box<dyn Error>> {
|
||||||
|
let file = File::open(path)?;
|
||||||
|
let reader = BufReader::new(file);
|
||||||
|
|
||||||
|
let mut cfg: Config = serde_yml::from_reader(reader)?;
|
||||||
|
cfg.sanitize()?;
|
||||||
|
for (name, acc) in cfg.accounts.iter_mut() {
|
||||||
|
match (
|
||||||
|
acc.hashed_password_.clone(),
|
||||||
|
acc.hashed_password_file_.clone(),
|
||||||
|
) {
|
||||||
|
(Some(_hash), Some(_file)) => {
|
||||||
|
return Err("Account {name} has both HashedPassword and HashedPasswordFile set, aborting...".into());
|
||||||
|
}
|
||||||
|
(Some(hash), None) => {
|
||||||
|
acc.hashed_password = hash;
|
||||||
|
}
|
||||||
|
(None, Some(file)) => {
|
||||||
|
let hash = fs::read_to_string(file.clone()).unwrap_or_else(|err| {
|
||||||
|
panic!("Account ({name}): Reading {file} failed: {err:?}")
|
||||||
|
});
|
||||||
|
acc.hashed_password = hash.trim().to_string();
|
||||||
|
}
|
||||||
|
(None, None) => {
|
||||||
|
return Err("Account {name} is missing HashedPassword or HashedPasswordFile, aborting...".into());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Ok(cfg)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn sanitize(&mut self) -> Result<(), Box<dyn Error>> {
|
||||||
|
// standardize capitalization, ...
|
||||||
|
sanitize_vec(&mut self.domains).unwrap_or_else(|err| panic!("domain: {err:?}"));
|
||||||
|
sanitize_map_keys(&mut self.accounts).unwrap_or_else(|err| panic!("accounts: {err:?}"));
|
||||||
|
sanitize_map_keys(&mut self.aliases).unwrap_or_else(|err| panic!("aliases: {err:?}"));
|
||||||
|
for (from, dests) in self.aliases.iter_mut() {
|
||||||
|
sanitize_vec(&mut *dests).unwrap_or_else(|err| panic!("aliases ({from}): {err:?}"));
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn check(&self) -> Result<(), Box<dyn Error>> {
|
||||||
|
// check whether all account domains exist
|
||||||
|
let re = Regex::new(r"^(?P<local_part>.*)@(?P<domain>.*)$").unwrap();
|
||||||
|
for name in self.accounts.keys() {
|
||||||
|
let caps = re
|
||||||
|
.captures(name)
|
||||||
|
.ok_or("Mail address regex does not match")
|
||||||
|
.unwrap();
|
||||||
|
if !self.domains.contains(&caps["domain"].to_string()) {
|
||||||
|
panic!("Domain of account \"{name}\" does not exist");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// check whether aliases have corresponding accounts/domains
|
||||||
|
for (from, dests) in self.aliases.iter() {
|
||||||
|
let is_domain = from.contains("@");
|
||||||
|
if is_domain && !self.accounts.contains_key(from) {
|
||||||
|
panic!("Aliased from-account \"{from}\" does not exist");
|
||||||
|
} else if !is_domain && !self.domains.contains(from) {
|
||||||
|
panic!("Aliased from-domain \"{from}\" does not exist");
|
||||||
|
}
|
||||||
|
for dest in dests.iter() {
|
||||||
|
let is_domain = dest.contains("@");
|
||||||
|
if is_domain && !self.accounts.contains_key(dest) {
|
||||||
|
panic!("Aliased dest-account \"{dest}\" does not exist");
|
||||||
|
} else if !is_domain && !self.domains.contains(dest) {
|
||||||
|
panic!("Aliased dest-domain \"{dest}\" does not exist");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn merge<P: AsRef<Path>>(&mut self, path: P) -> Result<(), Box<dyn Error>> {
|
||||||
|
let mut other = Config::load(path).unwrap();
|
||||||
|
|
||||||
|
if !self
|
||||||
|
.domains
|
||||||
|
.iter()
|
||||||
|
.all(|domain| other.domains.contains(domain))
|
||||||
|
{
|
||||||
|
return Err(
|
||||||
|
"domains: Duplicate entry during merge detected, aborting..."
|
||||||
|
.to_string()
|
||||||
|
.into(),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
self.domains.append(&mut other.domains);
|
||||||
|
|
||||||
|
if !self
|
||||||
|
.accounts
|
||||||
|
.keys()
|
||||||
|
.all(|name| other.accounts.contains_key(name))
|
||||||
|
{
|
||||||
|
return Err(
|
||||||
|
"accounts: Duplicate entry during merge detected, aborting..."
|
||||||
|
.to_string()
|
||||||
|
.into(),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
self.accounts.extend(other.accounts);
|
||||||
|
|
||||||
|
if !self
|
||||||
|
.aliases
|
||||||
|
.keys()
|
||||||
|
.all(|alias| other.aliases.contains_key(alias))
|
||||||
|
{
|
||||||
|
return Err(
|
||||||
|
"aliases: Duplicate entry during merge detected, aborting..."
|
||||||
|
.to_string()
|
||||||
|
.into(),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
self.aliases.extend(other.aliases);
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
55
pkgs/mailnix/src/dovecot.rs
Normal file
55
pkgs/mailnix/src/dovecot.rs
Normal file
|
|
@ -0,0 +1,55 @@
|
||||||
|
use regex::Regex;
|
||||||
|
use std::collections::HashMap;
|
||||||
|
use std::error::Error;
|
||||||
|
use std::fs;
|
||||||
|
use std::path::Path;
|
||||||
|
|
||||||
|
use crate::config::{AccountConfig, Config};
|
||||||
|
|
||||||
|
pub fn generate_userdb(cfg: Config) {
|
||||||
|
for name in cfg.accounts.into_keys() {
|
||||||
|
println!("{}:::::::", name);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn generate_static_passdb(cfg: Config) {
|
||||||
|
let system_accounts = cfg
|
||||||
|
.accounts
|
||||||
|
.into_iter()
|
||||||
|
.filter(|(_, acc)| acc.is_system_user);
|
||||||
|
for (name, _) in system_accounts {
|
||||||
|
println!("{}:::::::", name);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn update_dynamic_passdb<P: AsRef<Path>>(cfg: Config, path: P) -> Result<(), Box<dyn Error>> {
|
||||||
|
// create hashmap of all accounts with their initial passdb-lines
|
||||||
|
let mut accounts: HashMap<String, AccountConfig> = cfg
|
||||||
|
.accounts
|
||||||
|
.into_iter()
|
||||||
|
.filter(|(_, acc)| !acc.is_system_user)
|
||||||
|
.collect();
|
||||||
|
eprintln!("settings: {:#?}", accounts);
|
||||||
|
|
||||||
|
// load current passdb and update account password hashes
|
||||||
|
if path.as_ref().exists() {
|
||||||
|
let re = Regex::new(r"^(?P<name>.*):(?P<hashed_password>.*)::::::$").unwrap();
|
||||||
|
|
||||||
|
let curr_passdb = fs::read_to_string(path.as_ref()).unwrap();
|
||||||
|
eprintln!("current passdb: {curr_passdb}");
|
||||||
|
for line in curr_passdb.lines() {
|
||||||
|
//let caps = re.captures(line).ok_or_else(panic!("Regex does not match").unwrap();
|
||||||
|
let caps = re
|
||||||
|
.captures(line)
|
||||||
|
.unwrap_or_else(|| panic!("Regex does not match line: {line}"));
|
||||||
|
accounts.entry(caps["name"].to_string()).and_modify(|e| {
|
||||||
|
e.hashed_password = caps["hashed_password"].to_string();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for (name, acc) in accounts.into_iter() {
|
||||||
|
println!("{}:{}::::::", name, acc.hashed_password);
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
33
pkgs/mailnix/src/main.rs
Normal file
33
pkgs/mailnix/src/main.rs
Normal file
|
|
@ -0,0 +1,33 @@
|
||||||
|
mod cli;
|
||||||
|
mod config;
|
||||||
|
mod dovecot;
|
||||||
|
mod postfix;
|
||||||
|
|
||||||
|
use crate::{
|
||||||
|
cli::{Cli, Commands},
|
||||||
|
config::Config,
|
||||||
|
};
|
||||||
|
|
||||||
|
use clap::Parser;
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
let args = Cli::parse();
|
||||||
|
let mut cfg = Config::load(args.config_path).unwrap();
|
||||||
|
if let Some(additional_cfg_path) = args.additional_config_path {
|
||||||
|
cfg.merge(additional_cfg_path).unwrap();
|
||||||
|
}
|
||||||
|
let cfg = cfg;
|
||||||
|
cfg.check().unwrap();
|
||||||
|
|
||||||
|
match &args.command {
|
||||||
|
Commands::Check => println!("Check: {:#?}", cfg),
|
||||||
|
Commands::GenerateUserdb => dovecot::generate_userdb(cfg),
|
||||||
|
Commands::GenerateStaticPassdb => dovecot::generate_static_passdb(cfg),
|
||||||
|
Commands::UpdateDynamicPassdb { path } => {
|
||||||
|
dovecot::update_dynamic_passdb(cfg, path).unwrap()
|
||||||
|
}
|
||||||
|
Commands::GenerateAliases => postfix::generate_aliases(cfg),
|
||||||
|
Commands::GenerateDeniedRecipients => postfix::generate_denied_recipients(cfg),
|
||||||
|
Commands::GenerateDomains => postfix::generate_domains(cfg),
|
||||||
|
}
|
||||||
|
}
|
||||||
32
pkgs/mailnix/src/postfix.rs
Normal file
32
pkgs/mailnix/src/postfix.rs
Normal file
|
|
@ -0,0 +1,32 @@
|
||||||
|
use crate::config::Config;
|
||||||
|
|
||||||
|
fn prefix_domain(value: String) -> String {
|
||||||
|
if !value.contains("@") {
|
||||||
|
return format!("@{value}");
|
||||||
|
}
|
||||||
|
value
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn generate_aliases(cfg: Config) {
|
||||||
|
for name in cfg.accounts.into_keys() {
|
||||||
|
println!("{} {}", name, name);
|
||||||
|
}
|
||||||
|
for (src, dests) in cfg.aliases.into_iter() {
|
||||||
|
let dests: Vec<_> = dests.into_iter().map(prefix_domain).collect();
|
||||||
|
println!("{} {}", prefix_domain(src), dests.join(", "));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn generate_denied_recipients(cfg: Config) {
|
||||||
|
let system_accounts = cfg
|
||||||
|
.accounts
|
||||||
|
.into_iter()
|
||||||
|
.filter(|(_, acc)| acc.is_system_user);
|
||||||
|
for (name, acc) in system_accounts {
|
||||||
|
println!("{} REJECT {}", name, acc.reject_message);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn generate_domains(cfg: Config) {
|
||||||
|
println!("{}", cfg.domains.into_iter().collect::<Vec<_>>().join("\n"));
|
||||||
|
}
|
||||||
Loading…
Add table
Add a link
Reference in a new issue