├── src ├── db │ ├── migrations │ │ ├── .gitkeep │ │ ├── 2019-02-28-210537_allow_catchall │ │ │ ├── up.sql │ │ │ └── down.sql │ │ └── 2018-04-22-165901_create_vmaildb │ │ │ ├── down.sql │ │ │ └── up.sql │ ├── result.rs │ ├── database.rs │ ├── tlspolicy.rs │ ├── schema.rs │ ├── domain.rs │ ├── account.rs │ └── alias.rs ├── cmd.rs ├── result.rs ├── db.rs ├── utils.rs ├── crypt.rs ├── main.rs ├── cli.rs └── cmd │ ├── domain.rs │ ├── alias.rs │ └── user.rs ├── .gitignore ├── .github └── workflows │ ├── audit.yml │ ├── release.yml │ └── rust.yml ├── shell ├── gen_comp.sh ├── vmail-cli.fish ├── vmail-cli.bash └── _vmail-cli ├── CHANGELOG.md ├── Cargo.toml ├── LICENSE ├── README.md └── Cargo.lock /src/db/migrations/.gitkeep: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | /target 2 | vmail-lib/target 3 | **/*.rs.bk 4 | .env 5 | -------------------------------------------------------------------------------- /src/cmd.rs: -------------------------------------------------------------------------------- 1 | pub mod alias; 2 | pub mod domain; 3 | pub mod user; 4 | -------------------------------------------------------------------------------- /src/result.rs: -------------------------------------------------------------------------------- 1 | use failure::Error; 2 | use std::result; 3 | 4 | pub type Result = result::Result; 5 | -------------------------------------------------------------------------------- /src/db/migrations/2019-02-28-210537_allow_catchall/up.sql: -------------------------------------------------------------------------------- 1 | ALTER TABLE aliases 2 | MODIFY source_username VARCHAR(64) NULL; 3 | -------------------------------------------------------------------------------- /src/db/migrations/2019-02-28-210537_allow_catchall/down.sql: -------------------------------------------------------------------------------- 1 | ALTER TABLE aliases 2 | MODIFY source_username VARCHAR(64) NOT NULL; 3 | -------------------------------------------------------------------------------- /src/db/migrations/2018-04-22-165901_create_vmaildb/down.sql: -------------------------------------------------------------------------------- 1 | -- This file should undo anything in `up.sql` 2 | DROP TABLE domains; 3 | DROP TABLE accounts; 4 | DROP TABLE aliases; 5 | DROP TABLE tlspolicies; 6 | 7 | -------------------------------------------------------------------------------- /src/db/result.rs: -------------------------------------------------------------------------------- 1 | use failure::{Error, Fail}; 2 | use std::result; 3 | 4 | #[derive(Debug, Fail)] 5 | pub enum VmailError { 6 | #[fail(display = "Entry not found: {}", _0)] 7 | NotFound(String), 8 | } 9 | 10 | pub type Result = result::Result; 11 | -------------------------------------------------------------------------------- /.github/workflows/audit.yml: -------------------------------------------------------------------------------- 1 | name: Security audit 2 | on: 3 | push: 4 | paths: 5 | - '**/Cargo.toml' 6 | - '**/Cargo.lock' 7 | jobs: 8 | security_audit: 9 | runs-on: ubuntu-latest 10 | steps: 11 | - uses: actions/checkout@v1 12 | - uses: actions-rs/audit-check@v1 13 | with: 14 | token: ${{ secrets.GITHUB_TOKEN }} 15 | -------------------------------------------------------------------------------- /shell/gen_comp.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | APP_NAME="vmail-cli" 4 | OUT_PATH="shell" 5 | SHELLS=("zsh:_${APP_NAME}" "bash:${APP_NAME}.bash" "fish:${APP_NAME}.fish") 6 | 7 | for shell in ${SHELLS[@]}; do 8 | file="${shell#*:}" 9 | shell="${shell%:*}" 10 | path="$OUT_PATH/$file" 11 | echo "Produce $shell completion in $path" 12 | cargo run -- completions $shell > $path 13 | done 14 | -------------------------------------------------------------------------------- /src/db/database.rs: -------------------------------------------------------------------------------- 1 | use diesel::connection::Connection; 2 | 3 | #[cfg(not(feature = "postgres"))] 4 | pub use diesel::mysql::MysqlConnection as DatabaseConnection; 5 | #[cfg(feature = "postgres")] 6 | pub use diesel::pg::PgConnection as DatabaseConnection; 7 | 8 | pub fn connect(database_url: &str) -> DatabaseConnection { 9 | DatabaseConnection::establish(&database_url) 10 | .unwrap_or_else(|_| panic!("Error connecting to {}", database_url)) 11 | } 12 | -------------------------------------------------------------------------------- /src/db.rs: -------------------------------------------------------------------------------- 1 | mod database; 2 | pub mod result; 3 | 4 | pub mod account; 5 | pub mod alias; 6 | pub mod domain; 7 | pub mod schema; 8 | pub mod tlspolicy; 9 | 10 | use database::connect; 11 | pub use database::DatabaseConnection; 12 | use dotenv::dotenv; 13 | use std::env; 14 | 15 | pub fn establish_connection() -> DatabaseConnection { 16 | dotenv().ok(); 17 | 18 | let database_url = env::var("DATABASE_URL").expect("DATABASE_URL must be set"); 19 | 20 | connect(&database_url) 21 | } 22 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Changelog 2 | All notable changes to this project will be documented in this file. 3 | 4 | The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), 5 | and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). 6 | 7 | ## [Unreleased] 8 | 9 | ## [0.4.0] - 2021-01-13 10 | ### Changed 11 | - Merge vmail-lib into main package 12 | - Bump dependencies 13 | 14 | ## [0.3.1] - 2021-01-03 15 | ### Changed 16 | - sha-crypt to official release version (0.1.0) 17 | ### Fixed 18 | - clippy and fmt issues 19 | -------------------------------------------------------------------------------- /src/utils.rs: -------------------------------------------------------------------------------- 1 | use std::io; 2 | use std::io::prelude::*; 3 | 4 | #[derive(Debug, PartialEq)] 5 | pub enum YesNoAnswer { 6 | YES, 7 | NO, 8 | } 9 | 10 | pub fn yes_no(message: &str, default: YesNoAnswer) -> YesNoAnswer { 11 | let mut stdout = io::stdout(); 12 | 13 | print!("{} ", message); 14 | stdout.flush().unwrap(); 15 | 16 | let mut input = String::new(); 17 | io::stdin().read_line(&mut input).unwrap(); 18 | 19 | match input.trim() { 20 | "Yes" | "yes" | "Y" | "y" | "YeS" | "YES" => YesNoAnswer::YES, 21 | "No" | "NO" | "n" | "N" => YesNoAnswer::NO, 22 | _ => default, 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /src/crypt.rs: -------------------------------------------------------------------------------- 1 | use sha_crypt::{errors::CryptError, sha512_simple, Sha512Params}; 2 | use std::result::Result; 3 | 4 | pub enum PasswordScheme { 5 | Sha512Crypt, 6 | // TODO add some more and let the user decide, for now SHA512_CRYPT should 7 | // be alright 8 | } 9 | 10 | pub fn hash(scheme: &PasswordScheme, value: &str) -> Result { 11 | match scheme { 12 | PasswordScheme::Sha512Crypt => { 13 | let params = Sha512Params::default(); 14 | let mut pass = "{SHA512-CRYPT}".to_string(); 15 | pass += &sha512_simple(value, ¶ms)?; 16 | Ok(pass) 17 | } 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /src/db/tlspolicy.rs: -------------------------------------------------------------------------------- 1 | #![allow(proc_macro_derive_resolution_fallback)] 2 | use super::schema::tlspolicies; 3 | 4 | use diesel::{Insertable, Queryable}; 5 | use diesel_derive_enum::DbEnum; 6 | 7 | #[derive(DbEnum, Debug, PartialEq)] 8 | pub enum PolicyEnum { 9 | None, 10 | May, 11 | Encrypt, 12 | Dane, 13 | DaneOnly, 14 | Fingerprint, 15 | Verify, 16 | Secure, 17 | } 18 | 19 | #[derive(Queryable, Debug, PartialEq)] 20 | pub struct Tlspolicy { 21 | pub id: i32, 22 | pub domain: String, 23 | pub policy: PolicyEnum, 24 | pub params: Option, 25 | } 26 | 27 | #[derive(Insertable, Debug, PartialEq)] 28 | #[table_name = "tlspolicies"] 29 | pub struct NewTlspolicy { 30 | pub domain: String, 31 | pub policy: PolicyEnum, 32 | pub params: Option, 33 | } 34 | -------------------------------------------------------------------------------- /src/main.rs: -------------------------------------------------------------------------------- 1 | #[macro_use] 2 | extern crate diesel; 3 | 4 | use failure::format_err; 5 | use std::io; 6 | use std::process; 7 | 8 | mod cli; 9 | mod cmd; 10 | mod crypt; 11 | mod db; 12 | mod result; 13 | mod utils; 14 | 15 | fn main() { 16 | let matches = cli::build_cli().get_matches(); 17 | 18 | let r = match matches.subcommand() { 19 | ("user", Some(m)) => cmd::user::dispatch(m), 20 | ("alias", Some(m)) => cmd::alias::dispatch(m), 21 | ("domain", Some(m)) => cmd::domain::dispatch(m), 22 | ("completions", Some(m)) => { 23 | let shell = m.value_of("SHELL").unwrap(); 24 | cli::build_cli().gen_completions_to( 25 | "vmail-cli", 26 | shell.parse().unwrap(), 27 | &mut io::stdout(), 28 | ); 29 | Ok(()) 30 | } 31 | //("policies", Some(m)) => user(m), 32 | _ => Err(format_err!("Subcommand not implemented")), 33 | }; 34 | 35 | if let Err(e) = r { 36 | eprintln!("ERROR: {}", e); 37 | process::exit(-1) 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /.github/workflows/release.yml: -------------------------------------------------------------------------------- 1 | name: Release and publish 2 | 3 | on: 4 | push: 5 | branches: 6 | - "!*" 7 | tags: 8 | - "v*" 9 | paths-ignore: 10 | - README.md 11 | - CHANGELOG.md 12 | - .github/workflows/audit.yml 13 | - .github/workflows/rust.yml 14 | 15 | jobs: 16 | build: 17 | runs-on: ubuntu-latest 18 | 19 | steps: 20 | - name: Checkout 21 | uses: actions/checkout@v1 22 | 23 | - name: Install latest rust toolchain 24 | uses: actions-rs/toolchain@v1 25 | with: 26 | toolchain: stable 27 | default: true 28 | override: true 29 | 30 | - name: Build 31 | run: cargo build --all --release && strip target/release/vmail-cli && mv target/release/vmail-cli target/release/vmail-cli_amd64 32 | 33 | - name: Release 34 | uses: softprops/action-gh-release@v1 35 | if: startsWith(github.ref, 'refs/tags/v') 36 | with: 37 | files: | 38 | target/release/vmail-cli_amd64 39 | env: 40 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} 41 | -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "vmail-rs" 3 | version = "0.4.0" 4 | authors = [ "Armin Widegreen "] 5 | license = "BSD-3-Clause" 6 | documentation = "https://github.com/awidegreen/vmail-rs" 7 | homepage = "https://github.com/awidegreen/vmail-rs" 8 | repository = "https://github.com/awidegreen/vmail-rs" 9 | readme = "README.md" 10 | description = "A CLI client for vmail (mailserver setup with Dovecot, Postfix, MySQL, Rspamd)" 11 | edition = '2018' 12 | 13 | [[bin]] 14 | path = "src/main.rs" 15 | name = "vmail-cli" 16 | 17 | [features] 18 | default = ["mysql"] 19 | mysql = ["diesel/mysql", "diesel-derive-enum/mysql"] 20 | # Thomas Leistner's tutorial is using mysql, so this is the default database 21 | # backend. Build with `--features postgres` for postgres support. 22 | postgres = ["diesel/postgres", "diesel-derive-enum/postgres"] 23 | 24 | [dependencies] 25 | dotenv = "0.15.0" 26 | clap = "2.33.*" 27 | rpassword = "5.*" 28 | failure = "0.1" 29 | failure_derive = "0.1" 30 | sha-crypt = { version = "0.1.0", features = ["include_simple"] } 31 | diesel = { version = "1.4"} 32 | diesel-derive-enum = { version = "1.1"} 33 | -------------------------------------------------------------------------------- /src/cli.rs: -------------------------------------------------------------------------------- 1 | use clap::{crate_authors, crate_description, crate_version, App, AppSettings, Arg, SubCommand}; 2 | 3 | use crate::cmd::alias; 4 | use crate::cmd::domain; 5 | use crate::cmd::user; 6 | 7 | pub fn build_cli() -> App<'static, 'static> { 8 | App::new("vmail-cli") 9 | .version(crate_version!()) 10 | .author(crate_authors!("\n")) 11 | .about(crate_description!()) 12 | .global_setting(AppSettings::InferSubcommands) 13 | .setting(AppSettings::SubcommandRequiredElseHelp) 14 | .subcommand(user::get_subcommand()) 15 | .subcommand(domain::get_subcommand()) 16 | .subcommand(alias::get_subcommand()) 17 | .subcommand( 18 | SubCommand::with_name("completions") 19 | .about("Generates completion scripts for your shell") 20 | .setting(AppSettings::Hidden) 21 | .arg( 22 | Arg::with_name("SHELL") 23 | .required(true) 24 | .possible_values(&["bash", "fish", "zsh"]) 25 | .help("The shell to generate the script for"), 26 | ), 27 | ) 28 | } 29 | -------------------------------------------------------------------------------- /src/db/schema.rs: -------------------------------------------------------------------------------- 1 | #![allow(proc_macro_derive_resolution_fallback)] 2 | use super::tlspolicy::PolicyEnumMapping; 3 | 4 | table! { 5 | accounts (id) { 6 | id -> Integer, 7 | username -> Varchar, 8 | domain -> Varchar, 9 | password -> Varchar, 10 | quota -> Nullable, 11 | enabled -> Nullable, 12 | sendonly -> Nullable, 13 | } 14 | } 15 | 16 | table! { 17 | aliases (id) { 18 | id -> Integer, 19 | source_username -> Nullable, 20 | source_domain -> Varchar, 21 | destination_username -> Varchar, 22 | destination_domain -> Varchar, 23 | enabled -> Nullable, 24 | } 25 | } 26 | 27 | table! { 28 | domains (id) { 29 | id -> Integer, 30 | domain -> Varchar, 31 | } 32 | } 33 | 34 | table! { 35 | use diesel::sql_types::{Integer, Varchar, Nullable}; 36 | use super::PolicyEnumMapping; 37 | tlspolicies (id) { 38 | id -> Integer, 39 | domain -> Varchar, 40 | policy -> PolicyEnumMapping, 41 | params -> Nullable, 42 | } 43 | } 44 | 45 | allow_tables_to_appear_in_same_query!(accounts, aliases, domains, tlspolicies,); 46 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright 2021 Armin Widegreen 2 | 3 | Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: 4 | 5 | 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. 6 | 7 | 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. 8 | 9 | 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. 10 | 11 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 12 | -------------------------------------------------------------------------------- /src/db/migrations/2018-04-22-165901_create_vmaildb/up.sql: -------------------------------------------------------------------------------- 1 | -- Your SQL goes here 2 | CREATE TABLE `domains` ( 3 | `id` int unsigned NOT NULL AUTO_INCREMENT, 4 | `domain` varchar(255) NOT NULL, 5 | PRIMARY KEY (`id`), 6 | UNIQUE KEY (`domain`) 7 | ); 8 | 9 | CREATE TABLE `accounts` ( 10 | `id` int unsigned NOT NULL AUTO_INCREMENT, 11 | `username` varchar(64) NOT NULL, 12 | `domain` varchar(255) NOT NULL, 13 | `password` varchar(255) NOT NULL, 14 | `quota` int unsigned DEFAULT '0', 15 | `enabled` boolean DEFAULT '0', 16 | `sendonly` boolean DEFAULT '0', 17 | PRIMARY KEY (id), 18 | UNIQUE KEY (`username`, `domain`), 19 | FOREIGN KEY (`domain`) REFERENCES `domains` (`domain`) 20 | ); 21 | 22 | CREATE TABLE `aliases` ( 23 | `id` int unsigned NOT NULL AUTO_INCREMENT, 24 | `source_username` varchar(64) NOT NULL, 25 | `source_domain` varchar(255) NOT NULL, 26 | `destination_username` varchar(64) NOT NULL, 27 | `destination_domain` varchar(255) NOT NULL, 28 | `enabled` boolean DEFAULT '0', 29 | PRIMARY KEY (`id`), 30 | UNIQUE KEY (`source_username`, `source_domain`, `destination_username`, `destination_domain`), 31 | FOREIGN KEY (`source_domain`) REFERENCES `domains` (`domain`) 32 | ); 33 | 34 | CREATE TABLE `tlspolicies` ( 35 | `id` int unsigned NOT NULL AUTO_INCREMENT, 36 | `domain` varchar(255) NOT NULL, 37 | `policy` enum('none', 'may', 'encrypt', 'dane', 'dane-only', 'fingerprint', 'verify', 'secure') NOT NULL, 38 | `params` varchar(255), 39 | PRIMARY KEY (`id`), 40 | UNIQUE KEY (`domain`) 41 | ); 42 | -------------------------------------------------------------------------------- /src/db/domain.rs: -------------------------------------------------------------------------------- 1 | #![allow(proc_macro_derive_resolution_fallback)] 2 | use super::schema::domains; 3 | use diesel::prelude::*; 4 | use std::fmt; 5 | 6 | use super::database::DatabaseConnection; 7 | use super::result::{Result, VmailError}; 8 | 9 | use diesel::{Insertable, Queryable}; 10 | 11 | use failure::bail; 12 | 13 | #[derive(Queryable, PartialEq, Debug)] 14 | pub struct Domain { 15 | pub id: i32, 16 | pub domain: String, 17 | } 18 | 19 | #[derive(Insertable)] 20 | #[table_name = "domains"] 21 | pub struct NewDomain { 22 | pub domain: String, 23 | } 24 | 25 | impl fmt::Display for Domain { 26 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { 27 | write!(f, "{}", self.domain) 28 | } 29 | } 30 | 31 | impl Domain { 32 | pub fn all(conn: &DatabaseConnection) -> Result> { 33 | use super::schema::domains::dsl::*; 34 | 35 | let r = domains.load::(conn)?; 36 | Ok(r) 37 | } 38 | 39 | pub fn get(conn: &DatabaseConnection, name: &str) -> Result { 40 | use super::schema::domains::dsl::*; 41 | 42 | let mut r = domains 43 | .filter(domain.eq(name)) 44 | .limit(1) 45 | .load::(conn)?; 46 | 47 | if let Some(r) = r.pop() { 48 | return Ok(r); 49 | } 50 | 51 | bail!(VmailError::NotFound(format!("Domain {}", name))); 52 | } 53 | 54 | pub fn exsits(conn: &DatabaseConnection, name: &str) -> Result { 55 | use super::schema::domains::dsl::*; 56 | use diesel::dsl::exists; 57 | use diesel::select; 58 | 59 | let r = select(exists(domains.filter(domain.eq(name)))).get_result(conn)?; 60 | Ok(r) 61 | } 62 | 63 | /// returns number of rows inserted 64 | pub fn create(conn: &DatabaseConnection, domain: NewDomain) -> Result { 65 | use diesel::insert_into; 66 | 67 | let n = insert_into(domains::table).values(&domain).execute(conn)?; 68 | Ok(n) 69 | } 70 | 71 | /// returns number of rows deleted 72 | pub fn delete(conn: &DatabaseConnection, d: &Domain) -> Result { 73 | use super::schema::domains::dsl::*; 74 | use diesel::delete; 75 | 76 | let n = delete(domains.find(d.id)).execute(conn)?; 77 | Ok(n) 78 | } 79 | } 80 | -------------------------------------------------------------------------------- /.github/workflows/rust.yml: -------------------------------------------------------------------------------- 1 | name: ci 2 | 3 | on: 4 | pull_request: 5 | paths-ignore: 6 | - README.md 7 | push: 8 | branches: 9 | - '**' 10 | paths-ignore: 11 | - README.md 12 | - CHANGELOG.md 13 | - .github/workflows/release.yml 14 | - .github/workflows/audit.yml 15 | 16 | env: 17 | CARGO_INCREMENTAL: 0 18 | RUSTFLAGS: "-Dwarnings" 19 | CARGO_TERM_COLOR: always 20 | 21 | jobs: 22 | clippy-check: 23 | runs-on: ubuntu-latest 24 | steps: 25 | - uses: actions/checkout@v1 26 | - uses: actions-rs/toolchain@v1 27 | with: 28 | toolchain: nightly 29 | components: clippy 30 | override: true 31 | - uses: actions-rs/clippy-check@v1 32 | with: 33 | token: ${{ secrets.GITHUB_TOKEN }} 34 | args: --all-features 35 | 36 | rustfmt: 37 | runs-on: ubuntu-latest 38 | steps: 39 | - name: Checkout sources 40 | uses: actions/checkout@v1 41 | 42 | - name: Install stable toolchain 43 | uses: actions-rs/toolchain@v1 44 | with: 45 | profile: minimal 46 | toolchain: stable 47 | components: rustfmt 48 | 49 | - name: Run cargo fmt 50 | uses: actions-rs/cargo@v1 51 | with: 52 | command: fmt 53 | args: --all -- --check 54 | 55 | build: 56 | runs-on: ubuntu-latest 57 | strategy: 58 | matrix: 59 | rust: 60 | - stable 61 | target: 62 | #- thumbv7em-none-eabi TODO(awidegreen) 63 | - x86_64-unknown-linux-gnu 64 | steps: 65 | - uses: actions/checkout@v1 66 | - uses: actions-rs/toolchain@v1 67 | with: 68 | profile: minimal 69 | toolchain: ${{ matrix.rust }} 70 | target: ${{ matrix.target }} 71 | override: true 72 | - run: cargo build --target ${{ matrix.target }} --release --no-default-features --features mysql 73 | 74 | test: 75 | runs-on: ubuntu-latest 76 | strategy: 77 | matrix: 78 | rust: 79 | - stable 80 | steps: 81 | - uses: actions/checkout@v1 82 | - uses: actions-rs/toolchain@v1 83 | with: 84 | profile: minimal 85 | toolchain: ${{ matrix.rust }} 86 | override: true 87 | - run: cargo test --release --no-default-features --features mysql 88 | - run: cargo test --release 89 | -------------------------------------------------------------------------------- /src/db/account.rs: -------------------------------------------------------------------------------- 1 | #![allow(proc_macro_derive_resolution_fallback)] 2 | use super::domain::Domain; 3 | use super::schema::accounts; 4 | use diesel::prelude::*; 5 | use std::fmt; 6 | 7 | use super::database::DatabaseConnection; 8 | use super::result::{Result, VmailError}; 9 | 10 | use diesel::{AsChangeset, Identifiable, Insertable, Queryable}; 11 | 12 | use failure::bail; 13 | 14 | #[derive(Identifiable, AsChangeset, Queryable, PartialEq, Debug)] 15 | pub struct Account { 16 | pub id: i32, 17 | pub username: String, 18 | pub domain: String, 19 | pub password: String, 20 | pub quota: Option, 21 | pub enabled: Option, 22 | pub sendonly: Option, 23 | } 24 | 25 | #[derive(Insertable)] 26 | #[table_name = "accounts"] 27 | pub struct NewAccount<'a> { 28 | pub username: &'a str, 29 | pub domain: &'a str, 30 | pub password: &'a str, 31 | pub quota: Option, 32 | pub enabled: Option, 33 | pub sendonly: Option, 34 | } 35 | 36 | impl fmt::Display for Account { 37 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { 38 | let enabled = match self.enabled { 39 | Some(v) => v, 40 | _ => false, 41 | }; 42 | 43 | let sendonly = match self.sendonly { 44 | Some(v) => v, 45 | _ => false, 46 | }; 47 | 48 | let quota = match self.quota { 49 | Some(v) => v, 50 | _ => 0, 51 | }; 52 | 53 | write!( 54 | f, 55 | "{}@{}\nenabled: {}\nQuota: {}\nsend only: {}", 56 | self.username, self.domain, enabled, quota, sendonly 57 | ) 58 | } 59 | } 60 | 61 | impl Account { 62 | pub fn get(conn: &DatabaseConnection, name: &str, domain_: &str) -> Result { 63 | use super::schema::accounts::dsl::*; 64 | 65 | let mut r = accounts 66 | .filter(username.eq(name)) 67 | .filter(domain.eq(domain_)) 68 | .limit(1) 69 | .load::(conn)?; 70 | 71 | if let Some(r) = r.pop() { 72 | return Ok(r); 73 | } 74 | 75 | bail!(VmailError::NotFound(format!( 76 | "Account: {}@{}", 77 | name, domain_ 78 | ))); 79 | } 80 | 81 | pub fn all_by_username(conn: &DatabaseConnection, name: &str) -> Result> { 82 | use super::schema::accounts::dsl::*; 83 | 84 | let r = accounts 85 | .filter(username.eq(name)) 86 | .limit(1) 87 | .load::(conn)?; 88 | 89 | if !r.is_empty() { 90 | return Ok(r); 91 | } 92 | 93 | bail!(VmailError::NotFound(format!("Account username: {}", name))); 94 | } 95 | 96 | pub fn all_by_domain(conn: &DatabaseConnection, d: &Domain) -> Result> { 97 | use super::schema::accounts::dsl::*; 98 | 99 | let r = accounts 100 | .filter(domain.eq(&d.domain)) 101 | .load::(conn)?; 102 | 103 | if !r.is_empty() { 104 | return Ok(r); 105 | } 106 | 107 | bail!(VmailError::NotFound(format!( 108 | "Accounts domain: {}", 109 | &d.domain 110 | ))); 111 | } 112 | 113 | pub fn all(conn: &DatabaseConnection) -> Result> { 114 | use super::schema::accounts::dsl::*; 115 | let r = accounts.load::(conn)?; 116 | Ok(r) 117 | } 118 | 119 | /// returns number of rows inserted 120 | pub fn create(conn: &DatabaseConnection, account: NewAccount) -> Result { 121 | let n = diesel::insert_into(accounts::table) 122 | .values(account) 123 | .execute(conn)?; 124 | Ok(n) 125 | } 126 | 127 | /// returns number of rows deleted 128 | pub fn delete(conn: &DatabaseConnection, account: &Account) -> Result { 129 | use super::schema::accounts::dsl::*; 130 | 131 | let n = diesel::delete(accounts.find(account.id)).execute(conn)?; 132 | Ok(n) 133 | } 134 | 135 | pub fn exsits(conn: &DatabaseConnection, name: &str, domain_name: &str) -> bool { 136 | use super::schema::accounts::dsl::*; 137 | use diesel::dsl::exists; 138 | use diesel::select; 139 | 140 | let r = select(exists( 141 | accounts 142 | .filter(username.eq(name)) 143 | .filter(domain.eq(domain_name)), 144 | )) 145 | .get_result(conn); 146 | if let Ok(v) = r { 147 | v 148 | } else { 149 | false 150 | } 151 | } 152 | 153 | pub fn save(conn: &DatabaseConnection, account: &Account) -> Result { 154 | let n = diesel::update(account).set(account).execute(conn)?; 155 | Ok(n) 156 | } 157 | } 158 | -------------------------------------------------------------------------------- /src/db/alias.rs: -------------------------------------------------------------------------------- 1 | #![allow(proc_macro_derive_resolution_fallback)] 2 | 3 | use super::account::Account; 4 | use super::schema::aliases; 5 | use diesel::prelude::*; 6 | use diesel::{Insertable, Queryable}; 7 | use std::fmt; 8 | 9 | use failure::{bail, format_err}; 10 | 11 | use super::database::DatabaseConnection; 12 | use super::result::{Result, VmailError}; 13 | 14 | #[derive(Queryable, PartialEq, Debug)] 15 | pub struct Alias { 16 | pub id: i32, 17 | pub source_username: Option, 18 | pub source_domain: String, 19 | pub destination_username: String, 20 | pub destination_domain: String, 21 | pub enabled: Option, 22 | } 23 | 24 | #[derive(Insertable)] 25 | #[table_name = "aliases"] 26 | pub struct NewAlias { 27 | pub source_username: Option, 28 | pub source_domain: String, 29 | pub destination_username: String, 30 | pub destination_domain: String, 31 | pub enabled: Option, 32 | } 33 | 34 | impl fmt::Display for Alias { 35 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { 36 | let enabled = match self.enabled { 37 | Some(v) => v, 38 | _ => false, 39 | }; 40 | let username = self.source_username.as_deref().unwrap_or("%"); 41 | 42 | write!( 43 | f, 44 | "{}@{} => {}@{} (enabled: {})", 45 | username, 46 | self.source_domain, 47 | self.destination_username, 48 | self.destination_domain, 49 | enabled 50 | ) 51 | } 52 | } 53 | 54 | impl NewAlias { 55 | pub fn name(mut self, name: &str) -> Self { 56 | let name = if name == "%" { 57 | None 58 | } else { 59 | Some(String::from(name)) 60 | }; 61 | 62 | self.source_username = name; 63 | self 64 | } 65 | 66 | pub fn domain(mut self, domain: &str) -> Self { 67 | self.source_domain = String::from(domain); 68 | self.destination_domain = String::from(domain); 69 | self 70 | } 71 | 72 | pub fn target_domain(mut self, domain: &str) -> Self { 73 | self.destination_domain = String::from(domain); 74 | self 75 | } 76 | 77 | pub fn target_user(mut self, username: &str) -> Self { 78 | self.destination_username = String::from(username); 79 | self 80 | } 81 | 82 | pub fn enable(mut self, enabled: bool) -> Self { 83 | self.enabled = Some(enabled); 84 | self 85 | } 86 | } 87 | 88 | impl Alias { 89 | pub fn source_username(&self) -> String { 90 | self.source_username.as_deref().unwrap_or("%").to_string() 91 | } 92 | 93 | pub fn with_address(username: &str, domain: &str) -> NewAlias { 94 | Alias::new_alias().domain(domain).name(username) 95 | } 96 | 97 | pub fn new_alias() -> NewAlias { 98 | NewAlias { 99 | source_username: Some(String::new()), 100 | source_domain: String::new(), 101 | destination_username: String::new(), 102 | destination_domain: String::new(), 103 | enabled: Some(false), 104 | } 105 | } 106 | 107 | pub fn get(conn: &DatabaseConnection, name: &str, domain: &str) -> Result> { 108 | use super::schema::aliases::dsl::*; 109 | 110 | let r = if name == "%" { 111 | aliases 112 | .filter(source_username.is_null()) 113 | .filter(source_domain.eq(domain)) 114 | .load::(conn)? 115 | } else { 116 | aliases 117 | .filter(source_username.eq(name)) 118 | .filter(source_domain.eq(domain)) 119 | .load::(conn)? 120 | }; 121 | Ok(r) 122 | } 123 | 124 | pub fn all(conn: &DatabaseConnection) -> Result> { 125 | use super::schema::aliases::dsl::*; 126 | 127 | let r = aliases.load::(conn)?; 128 | Ok(r) 129 | } 130 | 131 | pub fn all_by_dest_account(conn: &DatabaseConnection, account: &Account) -> Result> { 132 | use super::schema::aliases::dsl::*; 133 | 134 | let r = aliases 135 | .filter(destination_username.eq(&account.username)) 136 | .filter(destination_domain.eq(&account.domain)) 137 | .load::(conn)?; 138 | 139 | if !r.is_empty() { 140 | return Ok(r); 141 | } 142 | 143 | bail!(VmailError::NotFound(format!( 144 | "Alias destination_username: {}, destination_domain: {}", 145 | account.username, account.domain 146 | ))); 147 | } 148 | 149 | pub fn delete(conn: &DatabaseConnection, alias: &Alias) -> Result { 150 | use super::schema::aliases::dsl::*; 151 | use diesel::delete; 152 | 153 | let n = delete(aliases.find(alias.id)).execute(conn)?; 154 | Ok(n) 155 | } 156 | 157 | pub fn create(conn: &DatabaseConnection, alias: &NewAlias) -> Result { 158 | use diesel::insert_into; 159 | 160 | match insert_into(aliases::table).values(alias).execute(conn) { 161 | Err(diesel::result::Error::DatabaseError(e, d)) => match e { 162 | diesel::result::DatabaseErrorKind::ForeignKeyViolation => Err(format_err!( 163 | "Either the referred domain or user account does not exist." 164 | )), 165 | other => Err(format_err!( 166 | "Other database error {:?}, details '{:?}'", 167 | other, 168 | d 169 | )), 170 | }, 171 | Err(e) => Err(format_err!("Other error: {}", e)), 172 | Ok(v) => Ok(v), 173 | } 174 | } 175 | 176 | pub fn exsits(conn: &DatabaseConnection, name: &str, domain_name: &str) -> bool { 177 | use super::schema::aliases::dsl::*; 178 | use diesel::dsl::exists; 179 | use diesel::select; 180 | 181 | let r = select(exists( 182 | aliases 183 | .filter(source_username.eq(name)) 184 | .filter(source_domain.eq(domain_name)), 185 | )) 186 | .get_result(conn); 187 | if let Ok(v) = r { 188 | v 189 | } else { 190 | false 191 | } 192 | } 193 | } 194 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # vmail-rs 2 | 3 | [![Build status](https://github.com/awidegreen/vmail-rs/workflows/ci/badge.svg)](https://github.com/awidegreen/vmail-rs/actions) 4 | [![License](https://img.shields.io/badge/license-BSD--3--Clause-blue)](LICENSE) 5 | [![Crates.io](https://img.shields.io/crates/v/vmail-rs.svg)](https://crates.io/crates/vmail-rs) 6 | 7 | `vmail-rs` is a command line tool for managing a mail-server database 8 | based on the great [HowTo](https://thomas-leister.de/en/mailserver-debian-stretch) ([german version](https://thomas-leister.de/mailserver-debian-stretch/)) 9 | from [Thomas Leister](https://thomas-leister.de) written in Rust. 10 | Although the tutorial is using a MySQL/MariaDB database, this command line tool 11 | also supports postgres. 12 | 13 | ## Prerequisites 14 | 15 | Make sure you have a working mail setup as described in the tutorial. 16 | You also need C libraries for your database client. For ubuntu, this is 17 | the `libmysqlclient-dev` package (or `libpq-dev`, if you are using postgres). 18 | 19 | Further, as `vmail-rs` is written in Rust, you should have a working 20 | [rustup/cargo setup](https://rustup.rs/). 21 | 22 | # Installation 23 | 24 | vmail-rs contains the cli tool `vmail-cli`. By default, MySQL/MariaDB is used. 25 | To enable postgres support, add `--features postgres` to the 26 | following `cargo` commands. 27 | 28 | ## From crates.io 29 | 30 | Install `vmail-rs` via `cargo`. 31 | 32 | ```sh 33 | > cargo install vmail-rs 34 | ``` 35 | 36 | ## From github 37 | 38 | ```sh 39 | # default mysql [default] 40 | > cargo install --git https://github.com/awidegreen/vmail-rs 41 | 42 | # use postgres 43 | > cargo install --features postgres --git https://github.com/awidegreen/vmail-rs 44 | ``` 45 | 46 | ## Build from sources 47 | 48 | Clone the repo and run 49 | 50 | ```sh 51 | > cargo install 52 | ``` 53 | 54 | or the release version 55 | 56 | ```sh 57 | > cargo install --release 58 | ``` 59 | 60 | # Usage 61 | 62 | vmail-rs uses Rust's [dotenv](https://docs.rs/crate/dotenv/) crate to create 63 | environment configuration from a `.env` file. 64 | 65 | Create a `.env` in the current directory containing the `DATABASE_URL` 66 | configuration parameter (be aware of the URI character encoding for the 67 | password). 68 | 69 | ``` 70 | DATABASE_URL=mysql://vmail:vmailpassword@localhost/database_name 71 | ``` 72 | 73 | If you are using postgres, a [key-value format](https://www.postgresql.org/docs/current/libpq-connect.html#LIBPQ-CONNSTRING) 74 | may be used instead of an URL. 75 | 76 | Use the command help to get started. 77 | 78 | ```shell 79 | vmail-cli --help 80 | ``` 81 | 82 | NOTE: all subcommands can also be shortcut'd. vmail-cli will automatically defer 83 | the correct command: `vmail-cli u s` equals `vmail-cli user show` 84 | 85 | The `user` subcommands can work on default domain. Therefore set 86 | `DEFAULT_DOMAIN=mydomain.org` in the `.env` file. More information below. 87 | 88 | ## Subcommand `domain` 89 | 90 | Available subcommands are: 91 | 92 | * add 93 | * help 94 | * remove 95 | * show 96 | 97 | Use help for more information. 98 | 99 | ``` 100 | # Create domain 101 | vmail-cli domain add mydomain.org 102 | 103 | # Show domains 104 | vmail-cli domain show 105 | 106 | # Delete domain 107 | vmail-cli domain remove mydomain.org 108 | ``` 109 | 110 | ## Subcommand `user` 111 | 112 | As the name suggests, this subcommand is used to mange the users/accounts within 113 | the database. In order to add a new user, the associated domain need to exist. 114 | 115 | Available subcommands are: 116 | 117 | * add 118 | * edit 119 | * help 120 | * password 121 | * remove 122 | * show 123 | 124 | Use help for more information. 125 | 126 | The `user` commands will assume a default domain, which can be set in `.env`: 127 | `DEFAULT_DOMAIN=mydomain.org`. If you want to use different domain, the 128 | parameter `--domain|-d` for the `user` subcommand should be used. Further, if no 129 | default domain has been set, the domain parameter needs to be provided. 130 | ``` 131 | # Create user for default domain 132 | vmail-cli user add foo 133 | 134 | # Create user for other domain 135 | vmail-cli user -d otherdomain.org add foo 136 | 137 | # Show users for default domain 138 | vmail-cli user show 139 | 140 | # Delete user for default domain 141 | vmail-cli user remove foo 142 | 143 | # Delete user other domain 144 | vmail-cli user -d otherdomain.org remove foo 145 | ``` 146 | 147 | ## Subcommand `alias` 148 | 149 | In order to add a new alias, the associated user and domain need to exist. 150 | 151 | Available subcommands are: 152 | 153 | * add 154 | * help 155 | * remove 156 | * show 157 | 158 | Use help for more information. 159 | 160 | ``` 161 | # Create alias 'bar@mydomain.org' for existing user-account 'foo@mydomain.org' 162 | vmail-cli alias add bar mydomain.org foo 163 | 164 | # Create alias 'hello@mydomain.org' for existing user-account 'foo@otherdomain.org' 165 | vmail-cli alias add hello mydomain.org foo otherdomain.org 166 | 167 | # Show aliases for all user accounts 168 | vmail-cli alias show 169 | 170 | # Show aliases for single user account 171 | vmail-cli alias show foo mydomain.org 172 | 173 | # Delete alias 'bar@mydomain.org' 174 | vmail-cli alias remove bar mydomain.org 175 | ``` 176 | In order to add/remove a catch-all alias for a domain, the '%' (percentage) 177 | should be used as a username for the alias. Make sure that your database 178 | supports [such feature](https://thomas-leister.de/mailserver-debian-stretch/#wie-kann-ich-mit-diesem-setup-catch-all-adressen-realisieren). 179 | 180 | ``` 181 | # Add catch-all alias for 'mydomain.org' domain (alias for user 'foo') 182 | vmail-cli alias add % mydomain.org foo 183 | 184 | # Remove catch-all alias for 'mydomain.org' domain 185 | vmail-cli alias remove % mydomain.org 186 | ``` 187 | 188 | # Misc 189 | 190 | ## Shell completions 191 | 192 | For bash, move `shell/vmail-cli.bash` to `$XDG_CONFIG_HOME/bash_completion/` or `/etc/bash_completion.d/`. 193 | 194 | For fish, move `shell/vmail-cli.fish` to `$HOME/.config/fish/completions/`. 195 | 196 | For zsh, move `shell/_vmail-cli` to one of your `$fpath` directories. 197 | 198 | For regenerating the shell completions, run `shell/gen_comp.sh` from the root of 199 | the repository. The files in `shell/` will be updated accordingly. This will use 200 | vmail-cli hidden `completions` subcommand. 201 | 202 | # How to contribute 203 | 204 | Create new issues if you find bugs or want to a new features. Pull requests are 205 | very welcomed. 206 | 207 | # License 208 | 209 | Copyright (C) 2021 by Armin Widegreen 210 | 211 | This is free software, licensed under The [BSD-3-Clause](LICENSE). 212 | -------------------------------------------------------------------------------- /src/cmd/domain.rs: -------------------------------------------------------------------------------- 1 | use crate::db::account::Account; 2 | use crate::db::alias::Alias; 3 | use crate::db::domain::{Domain, NewDomain}; 4 | use crate::db::{establish_connection, DatabaseConnection}; 5 | use crate::result::Result; 6 | use clap::{App, AppSettings, Arg, ArgMatches, SubCommand}; 7 | use failure::format_err; 8 | 9 | use crate::utils; 10 | 11 | //------------------------------------------------------------------------------ 12 | 13 | fn show(matches: &ArgMatches, conn: DatabaseConnection) -> Result<()> { 14 | let domain = matches.value_of("DOMAIN"); 15 | let with_users = matches.is_present("with-users"); 16 | let with_aliases = matches.is_present("with-aliases"); 17 | 18 | let domains = match domain { 19 | Some(domain) => vec![Domain::get(&conn, domain)?], 20 | _ => Domain::all(&conn)?, 21 | }; 22 | 23 | for d in domains { 24 | println!("{}", d); 25 | if with_users { 26 | if let Ok(accounts) = Account::all_by_domain(&conn, &d) { 27 | for a in accounts { 28 | println!(" {}@{}", a.username, a.domain); 29 | if with_aliases { 30 | if let Ok(aliases) = Alias::all_by_dest_account(&conn, &a) { 31 | for alias in aliases { 32 | println!(" {}", alias); 33 | } 34 | } 35 | } 36 | } 37 | } 38 | } 39 | } 40 | Ok(()) 41 | } 42 | 43 | //------------------------------------------------------------------------------ 44 | 45 | fn add(matches: &ArgMatches, conn: DatabaseConnection) -> Result<()> { 46 | let domain = matches.value_of("DOMAIN").unwrap(); 47 | 48 | if Domain::exsits(&conn, domain)? { 49 | return Err(format_err!( 50 | "The domain '{}' already exists, unable to add!", 51 | domain 52 | )); 53 | } 54 | let d = NewDomain { 55 | domain: String::from(domain), 56 | }; 57 | 58 | Domain::create(&conn, d)?; 59 | 60 | println!("Domain '{}' has been added!", domain); 61 | Ok(()) 62 | } 63 | 64 | //------------------------------------------------------------------------------ 65 | 66 | fn remove(matches: &ArgMatches, conn: DatabaseConnection) -> Result<()> { 67 | let domain_s = matches.value_of("DOMAIN").unwrap(); 68 | let force = matches.is_present("force"); 69 | let verbose = matches.is_present("verbose"); 70 | 71 | let domain = Domain::get(&conn, domain_s)?; 72 | 73 | let m = format!( 74 | "Shall the domain '{}' and all related user accounts really be removed? (y/N):", 75 | domain 76 | ); 77 | 78 | if !force && utils::yes_no(&m, utils::YesNoAnswer::NO) == utils::YesNoAnswer::NO { 79 | println!("Canceled removing domain '{}'", domain); 80 | return Ok(()); 81 | } 82 | 83 | if let Ok(accounts) = Account::all_by_domain(&conn, &domain) { 84 | for acc in accounts { 85 | if verbose { 86 | println!("Delete user: {}@{}", acc.username, acc.domain); 87 | } 88 | for alias in Alias::all_by_dest_account(&conn, &acc)? { 89 | if verbose { 90 | println!(" Delete alias: {}", alias); 91 | } 92 | Alias::delete(&conn, &alias)?; 93 | } 94 | Account::delete(&conn, &acc)?; 95 | } 96 | } 97 | Domain::delete(&conn, &domain)?; 98 | 99 | println!( 100 | "Domain '{}' and all its user accounts has been removed!", 101 | domain_s 102 | ); 103 | 104 | Ok(()) 105 | } 106 | 107 | //------------------------------------------------------------------------------ 108 | 109 | pub fn dispatch(matches: &ArgMatches) -> Result<()> { 110 | let conn = establish_connection(); 111 | 112 | match matches.subcommand() { 113 | ("show", Some(m)) => show(m, conn), 114 | ("add", Some(m)) => add(m, conn), 115 | ("remove", Some(m)) => remove(m, conn), 116 | _ => show(matches, conn), 117 | } 118 | } 119 | 120 | //------------------------------------------------------------------------------ 121 | 122 | pub fn get_subcommand() -> App<'static, 'static> { 123 | SubCommand::with_name("domain") 124 | .about("Manage domains for a vmail database") 125 | .setting(AppSettings::SubcommandRequiredElseHelp) 126 | .subcommand( 127 | SubCommand::with_name("show") 128 | .about("Show domains") 129 | .aliases(&["list", "ls"]) 130 | .arg(Arg::with_name("DOMAIN").help("Filter on domain")) 131 | .arg( 132 | Arg::with_name("with-users") 133 | .long("with-users") 134 | .short("u") 135 | .help("Show all users for the domain"), 136 | ) 137 | .arg( 138 | Arg::with_name("with-aliases") 139 | .long("with-aliases") 140 | .short("a") 141 | .requires("with-users") 142 | .help("Show all aliases for the users"), 143 | ), 144 | ) 145 | .subcommand( 146 | SubCommand::with_name("add") 147 | .about("Add a new domain to the database") 148 | .arg( 149 | Arg::with_name("DOMAIN") 150 | .required(true) 151 | .help("The domain name which should be added."), 152 | ), 153 | ) 154 | .subcommand( 155 | SubCommand::with_name("remove") 156 | .about( 157 | "Remove a domain from the database, this will also delete all related users.", 158 | ) 159 | .aliases(&["rm", "delete"]) 160 | .arg( 161 | Arg::with_name("force") 162 | .long("force") 163 | .short("f") 164 | .help("Force the deleting the given domain"), 165 | ) 166 | .arg( 167 | Arg::with_name("verbose") 168 | .long("verbose") 169 | .short("v") 170 | .help("Verbose output what has been deleted"), 171 | ) 172 | .arg( 173 | Arg::with_name("DOMAIN") 174 | .required(true) 175 | .help("The domain name which should be deleted."), 176 | ), 177 | ) 178 | } 179 | -------------------------------------------------------------------------------- /shell/vmail-cli.fish: -------------------------------------------------------------------------------- 1 | complete -c vmail-cli -n "__fish_use_subcommand" -s h -l help -d 'Prints help information' 2 | complete -c vmail-cli -n "__fish_use_subcommand" -s V -l version -d 'Prints version information' 3 | complete -c vmail-cli -n "__fish_use_subcommand" -f -a "user" -d 'User management for the vmail database' 4 | complete -c vmail-cli -n "__fish_use_subcommand" -f -a "domain" -d 'Manage domains for a vmail database' 5 | complete -c vmail-cli -n "__fish_use_subcommand" -f -a "alias" -d 'Manage aliases for the vmail database' 6 | complete -c vmail-cli -n "__fish_use_subcommand" -f -a "completions" -d 'Generates completion scripts for your shell' 7 | complete -c vmail-cli -n "__fish_use_subcommand" -f -a "help" -d 'Prints this message or the help of the given subcommand(s)' 8 | complete -c vmail-cli -n "__fish_seen_subcommand_from user" -s d -l domain -d 'Domain to use for the user subcommands, default can be specified in \'.env\' file. E.g. mydomain.tld' 9 | complete -c vmail-cli -n "__fish_seen_subcommand_from user" -s h -l help -d 'Prints help information' 10 | complete -c vmail-cli -n "__fish_seen_subcommand_from user" -s V -l version -d 'Prints version information' 11 | complete -c vmail-cli -n "__fish_seen_subcommand_from user" -f -a "show" -d 'Show user account(s)' 12 | complete -c vmail-cli -n "__fish_seen_subcommand_from user" -f -a "add" -d 'Add a user to the database' 13 | complete -c vmail-cli -n "__fish_seen_subcommand_from user" -f -a "remove" -d 'Remove a user from the database, will also delete all aliases for the user' 14 | complete -c vmail-cli -n "__fish_seen_subcommand_from user" -f -a "password" -d 'Change the password for given user' 15 | complete -c vmail-cli -n "__fish_seen_subcommand_from user" -f -a "edit" -d 'Edit a user account entry' 16 | complete -c vmail-cli -n "__fish_seen_subcommand_from user" -f -a "help" -d 'Prints this message or the help of the given subcommand(s)' 17 | complete -c vmail-cli -n "__fish_seen_subcommand_from show" -s v -l verbose -d 'Verbose output (include aliases)' 18 | complete -c vmail-cli -n "__fish_seen_subcommand_from show" -s h -l help -d 'Prints help information' 19 | complete -c vmail-cli -n "__fish_seen_subcommand_from show" -s V -l version -d 'Prints version information' 20 | complete -c vmail-cli -n "__fish_seen_subcommand_from add" -s q -l quota -d 'Quota for user account in MB (Megabyte), default is 0 which is unlimited' 21 | complete -c vmail-cli -n "__fish_seen_subcommand_from add" -s d -l disabled -d 'Disable the user, just add it to the database' 22 | complete -c vmail-cli -n "__fish_seen_subcommand_from add" -s s -l send-only -d 'Allow the new user only to send email but not receive any.' 23 | complete -c vmail-cli -n "__fish_seen_subcommand_from add" -s h -l help -d 'Prints help information' 24 | complete -c vmail-cli -n "__fish_seen_subcommand_from add" -s V -l version -d 'Prints version information' 25 | complete -c vmail-cli -n "__fish_seen_subcommand_from remove" -s f -l force -d 'Force the deleting the given user' 26 | complete -c vmail-cli -n "__fish_seen_subcommand_from remove" -s v -l verbose -d 'Verbose output what has been deleted' 27 | complete -c vmail-cli -n "__fish_seen_subcommand_from remove" -s h -l help -d 'Prints help information' 28 | complete -c vmail-cli -n "__fish_seen_subcommand_from remove" -s V -l version -d 'Prints version information' 29 | complete -c vmail-cli -n "__fish_seen_subcommand_from password" -s h -l help -d 'Prints help information' 30 | complete -c vmail-cli -n "__fish_seen_subcommand_from password" -s V -l version -d 'Prints version information' 31 | complete -c vmail-cli -n "__fish_seen_subcommand_from edit" -s q -l quota -d 'Quota for user account in MB (Megabyte), 0 is unlimited' 32 | complete -c vmail-cli -n "__fish_seen_subcommand_from edit" -s d -l disable -d 'Disable given user' 33 | complete -c vmail-cli -n "__fish_seen_subcommand_from edit" -s e -l enable -d 'Enable given user' 34 | complete -c vmail-cli -n "__fish_seen_subcommand_from edit" -s s -l send-only -d 'Allow user only to send' 35 | complete -c vmail-cli -n "__fish_seen_subcommand_from edit" -s r -l send-receive -d 'Allow user to send and receive' 36 | complete -c vmail-cli -n "__fish_seen_subcommand_from edit" -s h -l help -d 'Prints help information' 37 | complete -c vmail-cli -n "__fish_seen_subcommand_from edit" -s V -l version -d 'Prints version information' 38 | complete -c vmail-cli -n "__fish_seen_subcommand_from help" -s h -l help -d 'Prints help information' 39 | complete -c vmail-cli -n "__fish_seen_subcommand_from help" -s V -l version -d 'Prints version information' 40 | complete -c vmail-cli -n "__fish_seen_subcommand_from domain" -s h -l help -d 'Prints help information' 41 | complete -c vmail-cli -n "__fish_seen_subcommand_from domain" -s V -l version -d 'Prints version information' 42 | complete -c vmail-cli -n "__fish_seen_subcommand_from domain" -f -a "show" -d 'Show domains' 43 | complete -c vmail-cli -n "__fish_seen_subcommand_from domain" -f -a "add" -d 'Add a new domain to the database' 44 | complete -c vmail-cli -n "__fish_seen_subcommand_from domain" -f -a "remove" -d 'Remove a domain from the database, this will also delete all related users.' 45 | complete -c vmail-cli -n "__fish_seen_subcommand_from domain" -f -a "help" -d 'Prints this message or the help of the given subcommand(s)' 46 | complete -c vmail-cli -n "__fish_seen_subcommand_from show" -s u -l with-users -d 'Show all users for the domain' 47 | complete -c vmail-cli -n "__fish_seen_subcommand_from show" -s a -l with-aliases -d 'Show all aliases for the users' 48 | complete -c vmail-cli -n "__fish_seen_subcommand_from show" -s h -l help -d 'Prints help information' 49 | complete -c vmail-cli -n "__fish_seen_subcommand_from show" -s V -l version -d 'Prints version information' 50 | complete -c vmail-cli -n "__fish_seen_subcommand_from add" -s h -l help -d 'Prints help information' 51 | complete -c vmail-cli -n "__fish_seen_subcommand_from add" -s V -l version -d 'Prints version information' 52 | complete -c vmail-cli -n "__fish_seen_subcommand_from remove" -s f -l force -d 'Force the deleting the given domain' 53 | complete -c vmail-cli -n "__fish_seen_subcommand_from remove" -s v -l verbose -d 'Verbose output what has been deleted' 54 | complete -c vmail-cli -n "__fish_seen_subcommand_from remove" -s h -l help -d 'Prints help information' 55 | complete -c vmail-cli -n "__fish_seen_subcommand_from remove" -s V -l version -d 'Prints version information' 56 | complete -c vmail-cli -n "__fish_seen_subcommand_from help" -s h -l help -d 'Prints help information' 57 | complete -c vmail-cli -n "__fish_seen_subcommand_from help" -s V -l version -d 'Prints version information' 58 | complete -c vmail-cli -n "__fish_seen_subcommand_from alias" -s h -l help -d 'Prints help information' 59 | complete -c vmail-cli -n "__fish_seen_subcommand_from alias" -s V -l version -d 'Prints version information' 60 | complete -c vmail-cli -n "__fish_seen_subcommand_from alias" -f -a "show" -d 'Show aliases for user' 61 | complete -c vmail-cli -n "__fish_seen_subcommand_from alias" -f -a "add" -d 'Add an alias to an existing user account' 62 | complete -c vmail-cli -n "__fish_seen_subcommand_from alias" -f -a "remove" -d 'Remove an alias from the database' 63 | complete -c vmail-cli -n "__fish_seen_subcommand_from alias" -f -a "help" -d 'Prints this message or the help of the given subcommand(s)' 64 | complete -c vmail-cli -n "__fish_seen_subcommand_from show" -s h -l help -d 'Prints help information' 65 | complete -c vmail-cli -n "__fish_seen_subcommand_from show" -s V -l version -d 'Prints version information' 66 | complete -c vmail-cli -n "__fish_seen_subcommand_from add" -s d -l disabled -d 'Set alias to disabled' 67 | complete -c vmail-cli -n "__fish_seen_subcommand_from add" -s h -l help -d 'Prints help information' 68 | complete -c vmail-cli -n "__fish_seen_subcommand_from add" -s V -l version -d 'Prints version information' 69 | complete -c vmail-cli -n "__fish_seen_subcommand_from remove" -s h -l help -d 'Prints help information' 70 | complete -c vmail-cli -n "__fish_seen_subcommand_from remove" -s V -l version -d 'Prints version information' 71 | complete -c vmail-cli -n "__fish_seen_subcommand_from help" -s h -l help -d 'Prints help information' 72 | complete -c vmail-cli -n "__fish_seen_subcommand_from help" -s V -l version -d 'Prints version information' 73 | complete -c vmail-cli -n "__fish_seen_subcommand_from completions" -s h -l help -d 'Prints help information' 74 | complete -c vmail-cli -n "__fish_seen_subcommand_from completions" -s V -l version -d 'Prints version information' 75 | complete -c vmail-cli -n "__fish_seen_subcommand_from help" -s h -l help -d 'Prints help information' 76 | complete -c vmail-cli -n "__fish_seen_subcommand_from help" -s V -l version -d 'Prints version information' 77 | -------------------------------------------------------------------------------- /src/cmd/alias.rs: -------------------------------------------------------------------------------- 1 | use crate::db::account::Account; 2 | use crate::db::alias::Alias; 3 | use crate::db::result::Result; 4 | use crate::db::{establish_connection, DatabaseConnection}; 5 | use clap::{App, AppSettings, Arg, ArgMatches, SubCommand}; 6 | use failure::format_err; 7 | 8 | use crate::utils; 9 | 10 | //------------------------------------------------------------------------------ 11 | 12 | fn show(matches: &ArgMatches, conn: DatabaseConnection) -> Result<()> { 13 | let user = matches.value_of("DEST_USER"); 14 | let domain = matches.value_of("DEST_DOMAIN"); 15 | 16 | if let Some(user) = user { 17 | let domain = domain.unwrap(); 18 | let acc = Account::get(&conn, user, domain)?; 19 | println!("Filter for destination account: '{}@{}'", user, domain); 20 | for a in Alias::all_by_dest_account(&conn, &acc)? { 21 | println!("{}", a) 22 | } 23 | } else { 24 | let all = Alias::all(&conn)?; 25 | for a in all { 26 | println!("{}", a) 27 | } 28 | } 29 | 30 | Ok(()) 31 | } 32 | 33 | //------------------------------------------------------------------------------ 34 | 35 | fn add(matches: &ArgMatches, conn: DatabaseConnection) -> Result<()> { 36 | let enabled = !matches.is_present("disabled"); 37 | let forward = matches.is_present("forward"); 38 | let name = matches.value_of("USER").unwrap(); 39 | let domain = matches.value_of("DOMAIN").unwrap(); 40 | let dest_name = matches.value_of("DEST_USER").unwrap(); 41 | let dest_domain = matches.value_of("DEST_DOMAIN").unwrap_or(domain); 42 | 43 | if Account::exsits(&conn, name, domain) { 44 | return Err(format_err!( 45 | "Unable to add alias: '{}@{}' exists as a user-account!", 46 | name, 47 | domain 48 | )); 49 | } 50 | 51 | if !forward 52 | && !Account::exsits(&conn, dest_name, dest_domain) 53 | && !Alias::exsits(&conn, dest_name, dest_domain) 54 | { 55 | return Err(format_err!( 56 | "Unable to add alias '{}@{}' as destination '{}@{}' does not exists as user account nor as an existing alias!", 57 | name, 58 | domain, 59 | dest_name, 60 | dest_domain, 61 | )); 62 | } 63 | 64 | let a = Alias::with_address(name, domain) 65 | .target_domain(dest_domain) 66 | .target_user(dest_name) 67 | .enable(enabled); 68 | 69 | Alias::create(&conn, &a)?; 70 | 71 | println!( 72 | "Alias '{}@{}' for '{}@{}' has been added (enabled: {})!", 73 | name, domain, dest_name, dest_domain, enabled 74 | ); 75 | 76 | Ok(()) 77 | } 78 | 79 | //------------------------------------------------------------------------------ 80 | 81 | fn remove(matches: &ArgMatches, conn: DatabaseConnection) -> Result<()> { 82 | let user = matches.value_of("USER").unwrap(); 83 | let domain = matches.value_of("DOMAIN").unwrap(); 84 | let dest_user = matches.value_of("dest_user"); 85 | let dest_domain = matches.value_of("dest_domain"); 86 | let force = matches.is_present("force"); 87 | 88 | let aliases = Alias::get(&conn, user, domain)?; 89 | 90 | for alias in aliases { 91 | if let Some(dest_user) = dest_user { 92 | if alias.destination_username != dest_user { 93 | continue; 94 | } 95 | } 96 | if let Some(dest_domain) = dest_domain { 97 | if alias.destination_domain != dest_domain { 98 | continue; 99 | } 100 | } 101 | 102 | if !force { 103 | let m = format!("Shall the alias {} really be removed?", alias); 104 | if utils::yes_no(&m, utils::YesNoAnswer::NO) == utils::YesNoAnswer::NO { 105 | continue; 106 | } 107 | } 108 | Alias::delete(&conn, &alias)?; 109 | 110 | println!("Alias {} has been deleted!", alias); 111 | } 112 | 113 | Ok(()) 114 | } 115 | 116 | //------------------------------------------------------------------------------ 117 | 118 | pub fn dispatch(matches: &ArgMatches) -> Result<()> { 119 | let conn = establish_connection(); 120 | 121 | match matches.subcommand() { 122 | ("add", Some(m)) => add(m, conn), 123 | ("remove", Some(m)) => remove(m, conn), 124 | ("show", Some(m)) => show(m, conn), 125 | _ => show(matches, conn), 126 | } 127 | } 128 | 129 | //------------------------------------------------------------------------------ 130 | 131 | pub fn get_subcommand() -> App<'static, 'static> { 132 | SubCommand::with_name("alias") 133 | .about("Manage aliases for the vmail database") 134 | .setting(AppSettings::SubcommandRequiredElseHelp) 135 | .subcommand( 136 | SubCommand::with_name("show") 137 | .about("Show aliases for user") 138 | .aliases(&["list", "ls"]) 139 | .arg( 140 | Arg::with_name("DEST_USER") 141 | .requires("DEST_DOMAIN") 142 | .help("username to filter for"), 143 | ) 144 | .arg(Arg::with_name("DEST_DOMAIN").help("domain to filter for")), 145 | ) 146 | .subcommand( 147 | SubCommand::with_name("add") 148 | .about( 149 | "Add an alias to an existing user account. See USER help for catch-all aliases", 150 | ) 151 | .aliases(&["create", "new"]) 152 | .arg( 153 | Arg::with_name("USER") 154 | .required(true) 155 | .long_help( 156 | "Username for the alias. If the username is specified as '%' 157 | (percentage) it will act as a catch-all user.",)) 158 | .arg( 159 | Arg::with_name("DOMAIN") 160 | .required(true) 161 | .help("Existing domain for the alias"), 162 | ) 163 | .arg( 164 | Arg::with_name("DEST_USER") 165 | .required(true) 166 | .help("Existing user account"), 167 | ) 168 | .arg( 169 | Arg::with_name("DEST_DOMAIN") 170 | .help("If not specified, this will assume the value"), 171 | ) 172 | .arg( 173 | Arg::with_name("disabled") 174 | .long("disabled") 175 | .short("d") 176 | .help("Set alias to disabled"), 177 | ) 178 | .arg( 179 | Arg::with_name("forward") 180 | .long("forward") 181 | .short("f") 182 | .help("Allow alias to external domain"), 183 | ), 184 | ) 185 | .subcommand( 186 | SubCommand::with_name("remove") 187 | .about("Remove aliases from the database. See argument help for remove a catch-all user.") 188 | .alias("rm") 189 | .alias("delete") 190 | .arg( 191 | Arg::with_name("USER") 192 | .required(true) 193 | .long_help( 194 | "Username for the alias. For removing a catch-all alias, the '%' 195 | (percentage) should be used as a .")) 196 | .arg( 197 | Arg::with_name("DOMAIN") 198 | .required(true) 199 | .help("Domain of the alias (need to exist)"), 200 | ) 201 | .arg( 202 | Arg::with_name("dest_user") 203 | .value_name("dest_user") 204 | .long("destination-user") 205 | .short("u") 206 | .help("Filter on destination account/user name"), 207 | ) 208 | .arg( 209 | Arg::with_name("dest_domain") 210 | .value_name("dest_domain") 211 | .long("destination-domain") 212 | .short("d") 213 | .help("Filter on destination account/user domain"), 214 | ) 215 | .arg( 216 | Arg::with_name("force") 217 | .long("force") 218 | .short("f") 219 | .help("Delete the aliases without confirmation"), 220 | ), 221 | ) 222 | } 223 | -------------------------------------------------------------------------------- /src/cmd/user.rs: -------------------------------------------------------------------------------- 1 | use crate::crypt::{hash, PasswordScheme}; 2 | use crate::db::account::{Account, NewAccount}; 3 | use crate::db::alias::Alias; 4 | use crate::db::domain::Domain; 5 | use crate::db::result::Result; 6 | use crate::db::{establish_connection, DatabaseConnection}; 7 | use clap::{value_t, App, AppSettings, Arg, ArgMatches, SubCommand}; 8 | use dotenv::dotenv; 9 | use failure::format_err; 10 | use std::env; 11 | use std::process; 12 | 13 | use crate::utils; 14 | 15 | const DOMAIN_MISSING: &str = 16 | "A domain has to be provided via user command (arg '-d|--domain') or via '.env' file"; 17 | 18 | fn query_for_password() -> Option { 19 | let pass = rpassword::prompt_password_stdout("Password: ").unwrap(); 20 | if pass.is_empty() { 21 | eprintln!("Sorry, empty passwords are not allowed!"); 22 | return None; 23 | } 24 | let pass_confirm = rpassword::prompt_password_stdout("Confirm Password: ").unwrap(); 25 | 26 | if pass != pass_confirm { 27 | eprintln!("Sorry, passwords do not match, unable to proceed!"); 28 | return None; 29 | } 30 | 31 | Some(pass) 32 | } 33 | 34 | fn show(matches: &ArgMatches, conn: DatabaseConnection, domain: Option<&str>) -> Result<()> { 35 | let username = matches.value_of("USER").unwrap_or(""); 36 | let verbose = matches.is_present("verbose"); 37 | 38 | let accounts = if let Some(domain) = domain { 39 | let domain = Domain::get(&conn, &domain)?; 40 | println!("Show accounts for domain: {}", domain); 41 | Account::all_by_domain(&conn, &domain)? 42 | } else { 43 | Account::all(&conn)? 44 | }; 45 | 46 | for acc in accounts 47 | .iter() 48 | .filter(|a| username.is_empty() || a.username == username) 49 | { 50 | println!("{}", &acc); 51 | if verbose { 52 | if let Ok(aliases) = Alias::all_by_dest_account(&conn, acc) { 53 | println!("Aliases: "); 54 | for al in aliases { 55 | println!(" {}@{} ", al.source_username(), al.source_domain); 56 | } 57 | } 58 | } 59 | println!(); 60 | } 61 | 62 | Ok(()) 63 | } 64 | 65 | fn add(matches: &ArgMatches, conn: DatabaseConnection, domain: Option<&str>) -> Result<()> { 66 | let enabled = !matches.is_present("disabled"); 67 | let sendonly = matches.is_present("sendonly"); 68 | let username = matches.value_of("USER").unwrap(); 69 | let domain = domain.ok_or_else(|| format_err!("{}", DOMAIN_MISSING))?; 70 | 71 | let quota = value_t!(matches.value_of("quota"), i32).unwrap_or_else(|e| { 72 | eprintln!("Argument 'quota' has to be >= 0"); 73 | e.exit() 74 | }); 75 | 76 | if !Domain::exsits(&conn, domain)? { 77 | return Err(format_err!( 78 | "Unable to create user '{}' as the given domain '{}' does not exist!", 79 | username, 80 | domain 81 | )); 82 | } 83 | 84 | let pass = query_for_password().unwrap_or_else(|| process::exit(1)); 85 | let pass = hash(&PasswordScheme::Sha512Crypt, &pass).unwrap(); 86 | 87 | let a = NewAccount { 88 | username, 89 | domain, 90 | password: &pass, 91 | quota: Some(quota), 92 | enabled: Some(enabled), 93 | sendonly: Some(sendonly), 94 | }; 95 | 96 | Account::create(&conn, a)?; 97 | 98 | println!("User account '{}@{}' has been added!", username, domain); 99 | 100 | Ok(()) 101 | } 102 | 103 | fn remove(matches: &ArgMatches, conn: DatabaseConnection, domain: Option<&str>) -> Result<()> { 104 | let username = matches.value_of("USER").unwrap(); 105 | let domain = domain.ok_or_else(|| format_err!("{}", DOMAIN_MISSING))?; 106 | let verbose = matches.is_present("verbose"); 107 | let force = matches.is_present("force"); 108 | let m = format!( 109 | "Shall the user account '{}@{}' and all associated aliases be removed? (y/N):", 110 | username, domain 111 | ); 112 | 113 | let acc = Account::get(&conn, username, domain)?; 114 | 115 | if !force && utils::yes_no(&m, utils::YesNoAnswer::NO) == utils::YesNoAnswer::NO { 116 | println!( 117 | "Cancel removing user account '{}@{}' and all associated aliases?", 118 | username, domain 119 | ); 120 | return Ok(()); 121 | } 122 | 123 | // delete all aliases 124 | if let Ok(aliases) = Alias::all_by_dest_account(&conn, &acc) { 125 | for alias in aliases { 126 | if verbose { 127 | println!("Delete alias: {}", alias); 128 | } 129 | Alias::delete(&conn, &alias)?; 130 | } 131 | } 132 | 133 | Account::delete(&conn, &acc)?; 134 | 135 | println!( 136 | "User account '{}@{}' and all its aliases has been removed!", 137 | username, domain 138 | ); 139 | 140 | Ok(()) 141 | } 142 | 143 | fn password(matches: &ArgMatches, conn: DatabaseConnection, domain: Option<&str>) -> Result<()> { 144 | let username = matches.value_of("USER").unwrap(); 145 | let domain = domain.ok_or_else(|| format_err!("{}", DOMAIN_MISSING))?; 146 | 147 | let mut acc = Account::get(&conn, username, domain)?; 148 | 149 | let user = format!("{}@{}", username, domain); 150 | println!("Set password for '{}'", user); 151 | let pass = query_for_password().unwrap_or_else(|| process::exit(1)); 152 | let pass = hash(&PasswordScheme::Sha512Crypt, &pass).unwrap(); 153 | 154 | acc.password = pass; 155 | Account::save(&conn, &acc)?; 156 | 157 | println!("Password has been changed for {}@{}!", username, domain); 158 | Ok(()) 159 | } 160 | 161 | fn edit(matches: &ArgMatches, conn: DatabaseConnection, domain: Option<&str>) -> Result<()> { 162 | let username = matches.value_of("USER").unwrap(); 163 | let domain = domain.ok_or_else(|| format_err!("{}", DOMAIN_MISSING))?; 164 | 165 | let mut acc = Account::get(&conn, username, domain)?; 166 | 167 | if matches.is_present("enable") { 168 | acc.enabled = Some(true); 169 | } 170 | if matches.is_present("disable") { 171 | acc.enabled = Some(false); 172 | } 173 | if matches.is_present("sendonly") { 174 | acc.sendonly = Some(true); 175 | } 176 | if matches.is_present("sendreceive") { 177 | acc.sendonly = Some(false); 178 | } 179 | 180 | if matches.is_present("quota") { 181 | let quota = value_t!(matches.value_of("quota"), i32).unwrap_or_else(|e| { 182 | eprintln!("Argument 'quota' has to be >= 0"); 183 | e.exit() 184 | }); 185 | acc.quota = Some(quota); 186 | } 187 | 188 | Account::save(&conn, &acc)?; 189 | 190 | println!("Account updated: {}", acc); 191 | 192 | Ok(()) 193 | } 194 | 195 | pub fn dispatch(matches: &ArgMatches) -> Result<()> { 196 | dotenv().ok(); 197 | 198 | let default_domain = env::var("DEFAULT_DOMAIN"); 199 | let mut domain = if let Ok(domain) = default_domain.as_ref() { 200 | Some(domain.as_str()) 201 | } else { 202 | None 203 | }; 204 | if let Some(d) = matches.value_of("domain") { 205 | domain = Some(d) 206 | } 207 | 208 | let conn = establish_connection(); 209 | 210 | match matches.subcommand() { 211 | ("show", Some(m)) => show(m, conn, domain), 212 | ("add", Some(m)) => add(m, conn, domain), 213 | ("remove", Some(m)) => remove(m, conn, domain), 214 | ("password", Some(m)) => password(m, conn, domain), 215 | ("edit", Some(m)) => edit(m, conn, domain), 216 | _ => show(matches, conn, domain), 217 | } 218 | } 219 | 220 | pub fn get_subcommand() -> App<'static, 'static> { 221 | SubCommand::with_name("user") 222 | .about("User management for the vmail database") 223 | .setting(AppSettings::SubcommandRequiredElseHelp) 224 | .arg(Arg::with_name("domain") 225 | .value_name("domain") 226 | .long("domain") 227 | .short("d") 228 | .help("Domain to use for the user subcommands, default can be specified in '.env' file. E.g. mydomain.tld")) 229 | .subcommand(SubCommand::with_name("show") 230 | .about("Show user account(s)") 231 | .arg(Arg::with_name("USER") 232 | .help("Name of the user account which should be shown")) 233 | .arg(Arg::with_name("verbose") 234 | .long("verbose") 235 | .short("v") 236 | .help("Verbose output (include aliases)")) 237 | ) 238 | .subcommand(SubCommand::with_name("add") 239 | .about("Add a user to the database") 240 | .alias("create") 241 | .alias("new") 242 | .arg(Arg::with_name("USER") 243 | .required(true) 244 | .help("Name of the user which should be added, without domain name, e.g. 'newuser1'")) 245 | .arg(Arg::with_name("disabled") 246 | .long("disabled") 247 | .short("d") 248 | .help("Disable the user, just add it to the database")) 249 | .arg(Arg::with_name("sendonly") 250 | .long("send-only") 251 | .short("s") 252 | .help("Allow the new user only to send email but not receive any.")) 253 | .arg(Arg::with_name("quota") 254 | .value_name("quota") 255 | .long("quota") 256 | .short("q") 257 | .default_value("0") 258 | .help("Quota for user account in MB (Megabyte), default is 0 which is unlimited")) 259 | ) 260 | .subcommand(SubCommand::with_name("remove") 261 | .about("Remove a user from the database, will also delete all aliases for the user") 262 | .alias("rm") 263 | .alias("delete") 264 | .arg(Arg::with_name("force") 265 | .long("force") 266 | .short("f") 267 | .help("Force the deleting the given user")) 268 | .arg(Arg::with_name("verbose") 269 | .long("verbose") 270 | .short("v") 271 | .help("Verbose output what has been deleted")) 272 | .arg(Arg::with_name("USER") 273 | .required(true) 274 | .help("User which should be removed")) 275 | ) 276 | .subcommand(SubCommand::with_name("password") 277 | .about("Change the password for given user") 278 | .alias("pw") 279 | .arg(Arg::with_name("USER") 280 | .help("The user name which should be edited") 281 | .required(true)) 282 | ) 283 | .subcommand(SubCommand::with_name("edit") 284 | .about("Edit a user account entry") 285 | .alias("change") 286 | .alias("update") 287 | .arg(Arg::with_name("USER") 288 | .help("The user name which should be edited") 289 | .required(true)) 290 | .arg(Arg::with_name("disable") 291 | .long("disable") 292 | .short("d") 293 | .conflicts_with("enable") 294 | .help("Disable given user")) 295 | .arg(Arg::with_name("enable") 296 | .long("enable") 297 | .short("e") 298 | .conflicts_with("disable") 299 | .help("Enable given user")) 300 | .arg(Arg::with_name("sendonly") 301 | .long("send-only") 302 | .short("s") 303 | .conflicts_with("sendreceive") 304 | .help("Allow user only to send")) 305 | .arg(Arg::with_name("sendreceive") 306 | .long("send-receive") 307 | .short("r") 308 | .conflicts_with("sendonly") 309 | .help("Allow user to send and receive")) 310 | .arg(Arg::with_name("quota") 311 | .value_name("quota") 312 | .long("quota") 313 | .short("q") 314 | .help("Quota for user account in MB (Megabyte), 0 is unlimited")) 315 | ) 316 | } 317 | -------------------------------------------------------------------------------- /Cargo.lock: -------------------------------------------------------------------------------- 1 | # This file is automatically @generated by Cargo. 2 | # It is not intended for manual editing. 3 | [[package]] 4 | name = "addr2line" 5 | version = "0.14.1" 6 | source = "registry+https://github.com/rust-lang/crates.io-index" 7 | checksum = "a55f82cfe485775d02112886f4169bde0c5894d75e79ead7eafe7e40a25e45f7" 8 | dependencies = [ 9 | "gimli", 10 | ] 11 | 12 | [[package]] 13 | name = "adler" 14 | version = "0.2.3" 15 | source = "registry+https://github.com/rust-lang/crates.io-index" 16 | checksum = "ee2a4ec343196209d6594e19543ae87a39f96d5534d7174822a3ad825dd6ed7e" 17 | 18 | [[package]] 19 | name = "ansi_term" 20 | version = "0.11.0" 21 | source = "registry+https://github.com/rust-lang/crates.io-index" 22 | checksum = "ee49baf6cb617b853aa8d93bf420db2383fab46d314482ca2803b40d5fde979b" 23 | dependencies = [ 24 | "winapi", 25 | ] 26 | 27 | [[package]] 28 | name = "atty" 29 | version = "0.2.14" 30 | source = "registry+https://github.com/rust-lang/crates.io-index" 31 | checksum = "d9b39be18770d11421cdb1b9947a45dd3f37e93092cbf377614828a319d5fee8" 32 | dependencies = [ 33 | "hermit-abi", 34 | "libc", 35 | "winapi", 36 | ] 37 | 38 | [[package]] 39 | name = "autocfg" 40 | version = "1.0.1" 41 | source = "registry+https://github.com/rust-lang/crates.io-index" 42 | checksum = "cdb031dd78e28731d87d56cc8ffef4a8f36ca26c38fe2de700543e627f8a464a" 43 | 44 | [[package]] 45 | name = "backtrace" 46 | version = "0.3.55" 47 | source = "registry+https://github.com/rust-lang/crates.io-index" 48 | checksum = "ef5140344c85b01f9bbb4d4b7288a8aa4b3287ccef913a14bcc78a1063623598" 49 | dependencies = [ 50 | "addr2line", 51 | "cfg-if", 52 | "libc", 53 | "miniz_oxide", 54 | "object", 55 | "rustc-demangle", 56 | ] 57 | 58 | [[package]] 59 | name = "bitflags" 60 | version = "1.2.1" 61 | source = "registry+https://github.com/rust-lang/crates.io-index" 62 | checksum = "cf1de2fe8c75bc145a2f577add951f8134889b4795d47466a54a5c846d691693" 63 | 64 | [[package]] 65 | name = "block-buffer" 66 | version = "0.9.0" 67 | source = "registry+https://github.com/rust-lang/crates.io-index" 68 | checksum = "4152116fd6e9dadb291ae18fc1ec3575ed6d84c29642d97890f4b4a3417297e4" 69 | dependencies = [ 70 | "generic-array", 71 | ] 72 | 73 | [[package]] 74 | name = "byteorder" 75 | version = "1.3.4" 76 | source = "registry+https://github.com/rust-lang/crates.io-index" 77 | checksum = "08c48aae112d48ed9f069b33538ea9e3e90aa263cfa3d1c24309612b1f7472de" 78 | 79 | [[package]] 80 | name = "cfg-if" 81 | version = "1.0.0" 82 | source = "registry+https://github.com/rust-lang/crates.io-index" 83 | checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" 84 | 85 | [[package]] 86 | name = "clap" 87 | version = "2.33.3" 88 | source = "registry+https://github.com/rust-lang/crates.io-index" 89 | checksum = "37e58ac78573c40708d45522f0d80fa2f01cc4f9b4e2bf749807255454312002" 90 | dependencies = [ 91 | "ansi_term", 92 | "atty", 93 | "bitflags", 94 | "strsim", 95 | "textwrap", 96 | "unicode-width", 97 | "vec_map", 98 | ] 99 | 100 | [[package]] 101 | name = "cpuid-bool" 102 | version = "0.1.2" 103 | source = "registry+https://github.com/rust-lang/crates.io-index" 104 | checksum = "8aebca1129a03dc6dc2b127edd729435bbc4a37e1d5f4d7513165089ceb02634" 105 | 106 | [[package]] 107 | name = "diesel" 108 | version = "1.4.5" 109 | source = "registry+https://github.com/rust-lang/crates.io-index" 110 | checksum = "3e2de9deab977a153492a1468d1b1c0662c1cf39e5ea87d0c060ecd59ef18d8c" 111 | dependencies = [ 112 | "bitflags", 113 | "byteorder", 114 | "diesel_derives", 115 | "mysqlclient-sys", 116 | "pq-sys", 117 | "url", 118 | ] 119 | 120 | [[package]] 121 | name = "diesel-derive-enum" 122 | version = "1.1.0" 123 | source = "registry+https://github.com/rust-lang/crates.io-index" 124 | checksum = "703e71c268ea2d8da9c0ab0b40d8b217179ee622209c170875d24443193a0dfb" 125 | dependencies = [ 126 | "heck", 127 | "proc-macro2", 128 | "quote", 129 | "syn", 130 | ] 131 | 132 | [[package]] 133 | name = "diesel_derives" 134 | version = "1.4.1" 135 | source = "registry+https://github.com/rust-lang/crates.io-index" 136 | checksum = "45f5098f628d02a7a0f68ddba586fb61e80edec3bdc1be3b921f4ceec60858d3" 137 | dependencies = [ 138 | "proc-macro2", 139 | "quote", 140 | "syn", 141 | ] 142 | 143 | [[package]] 144 | name = "digest" 145 | version = "0.9.0" 146 | source = "registry+https://github.com/rust-lang/crates.io-index" 147 | checksum = "d3dd60d1080a57a05ab032377049e0591415d2b31afd7028356dbf3cc6dcb066" 148 | dependencies = [ 149 | "generic-array", 150 | ] 151 | 152 | [[package]] 153 | name = "dotenv" 154 | version = "0.15.0" 155 | source = "registry+https://github.com/rust-lang/crates.io-index" 156 | checksum = "77c90badedccf4105eca100756a0b1289e191f6fcbdadd3cee1d2f614f97da8f" 157 | 158 | [[package]] 159 | name = "failure" 160 | version = "0.1.8" 161 | source = "registry+https://github.com/rust-lang/crates.io-index" 162 | checksum = "d32e9bd16cc02eae7db7ef620b392808b89f6a5e16bb3497d159c6b92a0f4f86" 163 | dependencies = [ 164 | "backtrace", 165 | "failure_derive", 166 | ] 167 | 168 | [[package]] 169 | name = "failure_derive" 170 | version = "0.1.8" 171 | source = "registry+https://github.com/rust-lang/crates.io-index" 172 | checksum = "aa4da3c766cd7a0db8242e326e9e4e081edd567072893ed320008189715366a4" 173 | dependencies = [ 174 | "proc-macro2", 175 | "quote", 176 | "syn", 177 | "synstructure", 178 | ] 179 | 180 | [[package]] 181 | name = "generic-array" 182 | version = "0.14.4" 183 | source = "registry+https://github.com/rust-lang/crates.io-index" 184 | checksum = "501466ecc8a30d1d3b7fc9229b122b2ce8ed6e9d9223f1138d4babb253e51817" 185 | dependencies = [ 186 | "typenum", 187 | "version_check", 188 | ] 189 | 190 | [[package]] 191 | name = "getrandom" 192 | version = "0.1.16" 193 | source = "registry+https://github.com/rust-lang/crates.io-index" 194 | checksum = "8fc3cb4d91f53b50155bdcfd23f6a4c39ae1969c2ae85982b135750cccaf5fce" 195 | dependencies = [ 196 | "cfg-if", 197 | "libc", 198 | "wasi", 199 | ] 200 | 201 | [[package]] 202 | name = "gimli" 203 | version = "0.23.0" 204 | source = "registry+https://github.com/rust-lang/crates.io-index" 205 | checksum = "f6503fe142514ca4799d4c26297c4248239fe8838d827db6bd6065c6ed29a6ce" 206 | 207 | [[package]] 208 | name = "heck" 209 | version = "0.3.2" 210 | source = "registry+https://github.com/rust-lang/crates.io-index" 211 | checksum = "87cbf45460356b7deeb5e3415b5563308c0a9b057c85e12b06ad551f98d0a6ac" 212 | dependencies = [ 213 | "unicode-segmentation", 214 | ] 215 | 216 | [[package]] 217 | name = "hermit-abi" 218 | version = "0.1.17" 219 | source = "registry+https://github.com/rust-lang/crates.io-index" 220 | checksum = "5aca5565f760fb5b220e499d72710ed156fdb74e631659e99377d9ebfbd13ae8" 221 | dependencies = [ 222 | "libc", 223 | ] 224 | 225 | [[package]] 226 | name = "idna" 227 | version = "0.1.5" 228 | source = "registry+https://github.com/rust-lang/crates.io-index" 229 | checksum = "38f09e0f0b1fb55fdee1f17470ad800da77af5186a1a76c026b679358b7e844e" 230 | dependencies = [ 231 | "matches", 232 | "unicode-bidi", 233 | "unicode-normalization", 234 | ] 235 | 236 | [[package]] 237 | name = "libc" 238 | version = "0.2.81" 239 | source = "registry+https://github.com/rust-lang/crates.io-index" 240 | checksum = "1482821306169ec4d07f6aca392a4681f66c75c9918aa49641a2595db64053cb" 241 | 242 | [[package]] 243 | name = "matches" 244 | version = "0.1.8" 245 | source = "registry+https://github.com/rust-lang/crates.io-index" 246 | checksum = "7ffc5c5338469d4d3ea17d269fa8ea3512ad247247c30bd2df69e68309ed0a08" 247 | 248 | [[package]] 249 | name = "miniz_oxide" 250 | version = "0.4.3" 251 | source = "registry+https://github.com/rust-lang/crates.io-index" 252 | checksum = "0f2d26ec3309788e423cfbf68ad1800f061638098d76a83681af979dc4eda19d" 253 | dependencies = [ 254 | "adler", 255 | "autocfg", 256 | ] 257 | 258 | [[package]] 259 | name = "mysqlclient-sys" 260 | version = "0.2.4" 261 | source = "registry+https://github.com/rust-lang/crates.io-index" 262 | checksum = "7e9637d93448044078aaafea7419aed69d301b4a12bcc4aa0ae856eb169bef85" 263 | dependencies = [ 264 | "pkg-config", 265 | "vcpkg", 266 | ] 267 | 268 | [[package]] 269 | name = "object" 270 | version = "0.22.0" 271 | source = "registry+https://github.com/rust-lang/crates.io-index" 272 | checksum = "8d3b63360ec3cb337817c2dbd47ab4a0f170d285d8e5a2064600f3def1402397" 273 | 274 | [[package]] 275 | name = "opaque-debug" 276 | version = "0.3.0" 277 | source = "registry+https://github.com/rust-lang/crates.io-index" 278 | checksum = "624a8340c38c1b80fd549087862da4ba43e08858af025b236e509b6649fc13d5" 279 | 280 | [[package]] 281 | name = "percent-encoding" 282 | version = "1.0.1" 283 | source = "registry+https://github.com/rust-lang/crates.io-index" 284 | checksum = "31010dd2e1ac33d5b46a5b413495239882813e0369f8ed8a5e266f173602f831" 285 | 286 | [[package]] 287 | name = "pkg-config" 288 | version = "0.3.19" 289 | source = "registry+https://github.com/rust-lang/crates.io-index" 290 | checksum = "3831453b3449ceb48b6d9c7ad7c96d5ea673e9b470a1dc578c2ce6521230884c" 291 | 292 | [[package]] 293 | name = "ppv-lite86" 294 | version = "0.2.10" 295 | source = "registry+https://github.com/rust-lang/crates.io-index" 296 | checksum = "ac74c624d6b2d21f425f752262f42188365d7b8ff1aff74c82e45136510a4857" 297 | 298 | [[package]] 299 | name = "pq-sys" 300 | version = "0.4.6" 301 | source = "registry+https://github.com/rust-lang/crates.io-index" 302 | checksum = "6ac25eee5a0582f45a67e837e350d784e7003bd29a5f460796772061ca49ffda" 303 | dependencies = [ 304 | "vcpkg", 305 | ] 306 | 307 | [[package]] 308 | name = "proc-macro2" 309 | version = "1.0.24" 310 | source = "registry+https://github.com/rust-lang/crates.io-index" 311 | checksum = "1e0704ee1a7e00d7bb417d0770ea303c1bccbabf0ef1667dae92b5967f5f8a71" 312 | dependencies = [ 313 | "unicode-xid", 314 | ] 315 | 316 | [[package]] 317 | name = "quote" 318 | version = "1.0.8" 319 | source = "registry+https://github.com/rust-lang/crates.io-index" 320 | checksum = "991431c3519a3f36861882da93630ce66b52918dcf1b8e2fd66b397fc96f28df" 321 | dependencies = [ 322 | "proc-macro2", 323 | ] 324 | 325 | [[package]] 326 | name = "rand" 327 | version = "0.7.3" 328 | source = "registry+https://github.com/rust-lang/crates.io-index" 329 | checksum = "6a6b1679d49b24bbfe0c803429aa1874472f50d9b363131f0e89fc356b544d03" 330 | dependencies = [ 331 | "getrandom", 332 | "libc", 333 | "rand_chacha", 334 | "rand_core", 335 | "rand_hc", 336 | ] 337 | 338 | [[package]] 339 | name = "rand_chacha" 340 | version = "0.2.2" 341 | source = "registry+https://github.com/rust-lang/crates.io-index" 342 | checksum = "f4c8ed856279c9737206bf725bf36935d8666ead7aa69b52be55af369d193402" 343 | dependencies = [ 344 | "ppv-lite86", 345 | "rand_core", 346 | ] 347 | 348 | [[package]] 349 | name = "rand_core" 350 | version = "0.5.1" 351 | source = "registry+https://github.com/rust-lang/crates.io-index" 352 | checksum = "90bde5296fc891b0cef12a6d03ddccc162ce7b2aff54160af9338f8d40df6d19" 353 | dependencies = [ 354 | "getrandom", 355 | ] 356 | 357 | [[package]] 358 | name = "rand_hc" 359 | version = "0.2.0" 360 | source = "registry+https://github.com/rust-lang/crates.io-index" 361 | checksum = "ca3129af7b92a17112d59ad498c6f81eaf463253766b90396d39ea7a39d6613c" 362 | dependencies = [ 363 | "rand_core", 364 | ] 365 | 366 | [[package]] 367 | name = "rpassword" 368 | version = "5.0.0" 369 | source = "registry+https://github.com/rust-lang/crates.io-index" 370 | checksum = "d755237fc0f99d98641540e66abac8bc46a0652f19148ac9e21de2da06b326c9" 371 | dependencies = [ 372 | "libc", 373 | "winapi", 374 | ] 375 | 376 | [[package]] 377 | name = "rustc-demangle" 378 | version = "0.1.18" 379 | source = "registry+https://github.com/rust-lang/crates.io-index" 380 | checksum = "6e3bad0ee36814ca07d7968269dd4b7ec89ec2da10c4bb613928d3077083c232" 381 | 382 | [[package]] 383 | name = "sha-crypt" 384 | version = "0.1.0" 385 | source = "registry+https://github.com/rust-lang/crates.io-index" 386 | checksum = "06163938c59e73fff3474713dc31d8ecfdf1c204c16ffa931d632ad4486ee3a8" 387 | dependencies = [ 388 | "rand", 389 | "sha2", 390 | "subtle", 391 | ] 392 | 393 | [[package]] 394 | name = "sha2" 395 | version = "0.9.2" 396 | source = "registry+https://github.com/rust-lang/crates.io-index" 397 | checksum = "6e7aab86fe2149bad8c507606bdb3f4ef5e7b2380eb92350f56122cca72a42a8" 398 | dependencies = [ 399 | "block-buffer", 400 | "cfg-if", 401 | "cpuid-bool", 402 | "digest", 403 | "opaque-debug", 404 | ] 405 | 406 | [[package]] 407 | name = "strsim" 408 | version = "0.8.0" 409 | source = "registry+https://github.com/rust-lang/crates.io-index" 410 | checksum = "8ea5119cdb4c55b55d432abb513a0429384878c15dde60cc77b1c99de1a95a6a" 411 | 412 | [[package]] 413 | name = "subtle" 414 | version = "2.4.0" 415 | source = "registry+https://github.com/rust-lang/crates.io-index" 416 | checksum = "1e81da0851ada1f3e9d4312c704aa4f8806f0f9d69faaf8df2f3464b4a9437c2" 417 | 418 | [[package]] 419 | name = "syn" 420 | version = "1.0.57" 421 | source = "registry+https://github.com/rust-lang/crates.io-index" 422 | checksum = "4211ce9909eb971f111059df92c45640aad50a619cf55cd76476be803c4c68e6" 423 | dependencies = [ 424 | "proc-macro2", 425 | "quote", 426 | "unicode-xid", 427 | ] 428 | 429 | [[package]] 430 | name = "synstructure" 431 | version = "0.12.4" 432 | source = "registry+https://github.com/rust-lang/crates.io-index" 433 | checksum = "b834f2d66f734cb897113e34aaff2f1ab4719ca946f9a7358dba8f8064148701" 434 | dependencies = [ 435 | "proc-macro2", 436 | "quote", 437 | "syn", 438 | "unicode-xid", 439 | ] 440 | 441 | [[package]] 442 | name = "textwrap" 443 | version = "0.11.0" 444 | source = "registry+https://github.com/rust-lang/crates.io-index" 445 | checksum = "d326610f408c7a4eb6f51c37c330e496b08506c9457c9d34287ecc38809fb060" 446 | dependencies = [ 447 | "unicode-width", 448 | ] 449 | 450 | [[package]] 451 | name = "tinyvec" 452 | version = "1.1.0" 453 | source = "registry+https://github.com/rust-lang/crates.io-index" 454 | checksum = "ccf8dbc19eb42fba10e8feaaec282fb50e2c14b2726d6301dbfeed0f73306a6f" 455 | dependencies = [ 456 | "tinyvec_macros", 457 | ] 458 | 459 | [[package]] 460 | name = "tinyvec_macros" 461 | version = "0.1.0" 462 | source = "registry+https://github.com/rust-lang/crates.io-index" 463 | checksum = "cda74da7e1a664f795bb1f8a87ec406fb89a02522cf6e50620d016add6dbbf5c" 464 | 465 | [[package]] 466 | name = "typenum" 467 | version = "1.12.0" 468 | source = "registry+https://github.com/rust-lang/crates.io-index" 469 | checksum = "373c8a200f9e67a0c95e62a4f52fbf80c23b4381c05a17845531982fa99e6b33" 470 | 471 | [[package]] 472 | name = "unicode-bidi" 473 | version = "0.3.4" 474 | source = "registry+https://github.com/rust-lang/crates.io-index" 475 | checksum = "49f2bd0c6468a8230e1db229cff8029217cf623c767ea5d60bfbd42729ea54d5" 476 | dependencies = [ 477 | "matches", 478 | ] 479 | 480 | [[package]] 481 | name = "unicode-normalization" 482 | version = "0.1.16" 483 | source = "registry+https://github.com/rust-lang/crates.io-index" 484 | checksum = "a13e63ab62dbe32aeee58d1c5408d35c36c392bba5d9d3142287219721afe606" 485 | dependencies = [ 486 | "tinyvec", 487 | ] 488 | 489 | [[package]] 490 | name = "unicode-segmentation" 491 | version = "1.7.1" 492 | source = "registry+https://github.com/rust-lang/crates.io-index" 493 | checksum = "bb0d2e7be6ae3a5fa87eed5fb451aff96f2573d2694942e40543ae0bbe19c796" 494 | 495 | [[package]] 496 | name = "unicode-width" 497 | version = "0.1.8" 498 | source = "registry+https://github.com/rust-lang/crates.io-index" 499 | checksum = "9337591893a19b88d8d87f2cec1e73fad5cdfd10e5a6f349f498ad6ea2ffb1e3" 500 | 501 | [[package]] 502 | name = "unicode-xid" 503 | version = "0.2.1" 504 | source = "registry+https://github.com/rust-lang/crates.io-index" 505 | checksum = "f7fe0bb3479651439c9112f72b6c505038574c9fbb575ed1bf3b797fa39dd564" 506 | 507 | [[package]] 508 | name = "url" 509 | version = "1.7.2" 510 | source = "registry+https://github.com/rust-lang/crates.io-index" 511 | checksum = "dd4e7c0d531266369519a4aa4f399d748bd37043b00bde1e4ff1f60a120b355a" 512 | dependencies = [ 513 | "idna", 514 | "matches", 515 | "percent-encoding", 516 | ] 517 | 518 | [[package]] 519 | name = "vcpkg" 520 | version = "0.2.11" 521 | source = "registry+https://github.com/rust-lang/crates.io-index" 522 | checksum = "b00bca6106a5e23f3eee943593759b7fcddb00554332e856d990c893966879fb" 523 | 524 | [[package]] 525 | name = "vec_map" 526 | version = "0.8.2" 527 | source = "registry+https://github.com/rust-lang/crates.io-index" 528 | checksum = "f1bddf1187be692e79c5ffeab891132dfb0f236ed36a43c7ed39f1165ee20191" 529 | 530 | [[package]] 531 | name = "version_check" 532 | version = "0.9.2" 533 | source = "registry+https://github.com/rust-lang/crates.io-index" 534 | checksum = "b5a972e5669d67ba988ce3dc826706fb0a8b01471c088cb0b6110b805cc36aed" 535 | 536 | [[package]] 537 | name = "vmail-rs" 538 | version = "0.4.0" 539 | dependencies = [ 540 | "clap", 541 | "diesel", 542 | "diesel-derive-enum", 543 | "dotenv", 544 | "failure", 545 | "failure_derive", 546 | "rpassword", 547 | "sha-crypt", 548 | ] 549 | 550 | [[package]] 551 | name = "wasi" 552 | version = "0.9.0+wasi-snapshot-preview1" 553 | source = "registry+https://github.com/rust-lang/crates.io-index" 554 | checksum = "cccddf32554fecc6acb585f82a32a72e28b48f8c4c1883ddfeeeaa96f7d8e519" 555 | 556 | [[package]] 557 | name = "winapi" 558 | version = "0.3.9" 559 | source = "registry+https://github.com/rust-lang/crates.io-index" 560 | checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419" 561 | dependencies = [ 562 | "winapi-i686-pc-windows-gnu", 563 | "winapi-x86_64-pc-windows-gnu", 564 | ] 565 | 566 | [[package]] 567 | name = "winapi-i686-pc-windows-gnu" 568 | version = "0.4.0" 569 | source = "registry+https://github.com/rust-lang/crates.io-index" 570 | checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" 571 | 572 | [[package]] 573 | name = "winapi-x86_64-pc-windows-gnu" 574 | version = "0.4.0" 575 | source = "registry+https://github.com/rust-lang/crates.io-index" 576 | checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" 577 | -------------------------------------------------------------------------------- /shell/vmail-cli.bash: -------------------------------------------------------------------------------- 1 | _vmail-cli() { 2 | local i cur prev opts cmds 3 | COMPREPLY=() 4 | cur="${COMP_WORDS[COMP_CWORD]}" 5 | prev="${COMP_WORDS[COMP_CWORD-1]}" 6 | cmd="" 7 | opts="" 8 | 9 | for i in ${COMP_WORDS[@]} 10 | do 11 | case "${i}" in 12 | vmail-cli) 13 | cmd="vmail-cli" 14 | ;; 15 | 16 | add) 17 | cmd+="__add" 18 | ;; 19 | alias) 20 | cmd+="__alias" 21 | ;; 22 | change) 23 | cmd+="__change" 24 | ;; 25 | completions) 26 | cmd+="__completions" 27 | ;; 28 | create) 29 | cmd+="__create" 30 | ;; 31 | delete) 32 | cmd+="__delete" 33 | ;; 34 | domain) 35 | cmd+="__domain" 36 | ;; 37 | edit) 38 | cmd+="__edit" 39 | ;; 40 | help) 41 | cmd+="__help" 42 | ;; 43 | list) 44 | cmd+="__list" 45 | ;; 46 | ls) 47 | cmd+="__ls" 48 | ;; 49 | new) 50 | cmd+="__new" 51 | ;; 52 | password) 53 | cmd+="__password" 54 | ;; 55 | pw) 56 | cmd+="__pw" 57 | ;; 58 | remove) 59 | cmd+="__remove" 60 | ;; 61 | rm) 62 | cmd+="__rm" 63 | ;; 64 | show) 65 | cmd+="__show" 66 | ;; 67 | update) 68 | cmd+="__update" 69 | ;; 70 | user) 71 | cmd+="__user" 72 | ;; 73 | *) 74 | ;; 75 | esac 76 | done 77 | 78 | case "${cmd}" in 79 | vmail-cli) 80 | opts=" -h -V --help --version user domain alias completions help" 81 | if [[ ${cur} == -* || ${COMP_CWORD} -eq 1 ]] ; then 82 | COMPREPLY=( $(compgen -W "${opts}" -- ${cur}) ) 83 | return 0 84 | fi 85 | case "${prev}" in 86 | 87 | *) 88 | COMPREPLY=() 89 | ;; 90 | esac 91 | COMPREPLY=( $(compgen -W "${opts}" -- ${cur}) ) 92 | return 0 93 | ;; 94 | 95 | vmail__cli__alias) 96 | opts=" -h -V --help --version show add remove help list ls create new rm delete" 97 | if [[ ${cur} == -* || ${COMP_CWORD} -eq 2 ]] ; then 98 | COMPREPLY=( $(compgen -W "${opts}" -- ${cur}) ) 99 | return 0 100 | fi 101 | case "${prev}" in 102 | 103 | *) 104 | COMPREPLY=() 105 | ;; 106 | esac 107 | COMPREPLY=( $(compgen -W "${opts}" -- ${cur}) ) 108 | return 0 109 | ;; 110 | vmail__cli__alias__add) 111 | opts=" -d -h -V --disabled --help --version " 112 | if [[ ${cur} == -* || ${COMP_CWORD} -eq 3 ]] ; then 113 | COMPREPLY=( $(compgen -W "${opts}" -- ${cur}) ) 114 | return 0 115 | fi 116 | case "${prev}" in 117 | 118 | *) 119 | COMPREPLY=() 120 | ;; 121 | esac 122 | COMPREPLY=( $(compgen -W "${opts}" -- ${cur}) ) 123 | return 0 124 | ;; 125 | vmail__cli__alias__create) 126 | opts=" -d -h -V --disabled --help --version " 127 | if [[ ${cur} == -* || ${COMP_CWORD} -eq 3 ]] ; then 128 | COMPREPLY=( $(compgen -W "${opts}" -- ${cur}) ) 129 | return 0 130 | fi 131 | case "${prev}" in 132 | 133 | *) 134 | COMPREPLY=() 135 | ;; 136 | esac 137 | COMPREPLY=( $(compgen -W "${opts}" -- ${cur}) ) 138 | return 0 139 | ;; 140 | vmail__cli__alias__delete) 141 | opts=" -h -V --help --version " 142 | if [[ ${cur} == -* || ${COMP_CWORD} -eq 3 ]] ; then 143 | COMPREPLY=( $(compgen -W "${opts}" -- ${cur}) ) 144 | return 0 145 | fi 146 | case "${prev}" in 147 | 148 | *) 149 | COMPREPLY=() 150 | ;; 151 | esac 152 | COMPREPLY=( $(compgen -W "${opts}" -- ${cur}) ) 153 | return 0 154 | ;; 155 | vmail__cli__alias__help) 156 | opts=" -h -V --help --version " 157 | if [[ ${cur} == -* || ${COMP_CWORD} -eq 3 ]] ; then 158 | COMPREPLY=( $(compgen -W "${opts}" -- ${cur}) ) 159 | return 0 160 | fi 161 | case "${prev}" in 162 | 163 | *) 164 | COMPREPLY=() 165 | ;; 166 | esac 167 | COMPREPLY=( $(compgen -W "${opts}" -- ${cur}) ) 168 | return 0 169 | ;; 170 | vmail__cli__alias__list) 171 | opts=" -h -V --help --version " 172 | if [[ ${cur} == -* || ${COMP_CWORD} -eq 3 ]] ; then 173 | COMPREPLY=( $(compgen -W "${opts}" -- ${cur}) ) 174 | return 0 175 | fi 176 | case "${prev}" in 177 | 178 | *) 179 | COMPREPLY=() 180 | ;; 181 | esac 182 | COMPREPLY=( $(compgen -W "${opts}" -- ${cur}) ) 183 | return 0 184 | ;; 185 | vmail__cli__alias__ls) 186 | opts=" -h -V --help --version " 187 | if [[ ${cur} == -* || ${COMP_CWORD} -eq 3 ]] ; then 188 | COMPREPLY=( $(compgen -W "${opts}" -- ${cur}) ) 189 | return 0 190 | fi 191 | case "${prev}" in 192 | 193 | *) 194 | COMPREPLY=() 195 | ;; 196 | esac 197 | COMPREPLY=( $(compgen -W "${opts}" -- ${cur}) ) 198 | return 0 199 | ;; 200 | vmail__cli__alias__new) 201 | opts=" -d -h -V --disabled --help --version " 202 | if [[ ${cur} == -* || ${COMP_CWORD} -eq 3 ]] ; then 203 | COMPREPLY=( $(compgen -W "${opts}" -- ${cur}) ) 204 | return 0 205 | fi 206 | case "${prev}" in 207 | 208 | *) 209 | COMPREPLY=() 210 | ;; 211 | esac 212 | COMPREPLY=( $(compgen -W "${opts}" -- ${cur}) ) 213 | return 0 214 | ;; 215 | vmail__cli__alias__remove) 216 | opts=" -h -V --help --version " 217 | if [[ ${cur} == -* || ${COMP_CWORD} -eq 3 ]] ; then 218 | COMPREPLY=( $(compgen -W "${opts}" -- ${cur}) ) 219 | return 0 220 | fi 221 | case "${prev}" in 222 | 223 | *) 224 | COMPREPLY=() 225 | ;; 226 | esac 227 | COMPREPLY=( $(compgen -W "${opts}" -- ${cur}) ) 228 | return 0 229 | ;; 230 | vmail__cli__alias__rm) 231 | opts=" -h -V --help --version " 232 | if [[ ${cur} == -* || ${COMP_CWORD} -eq 3 ]] ; then 233 | COMPREPLY=( $(compgen -W "${opts}" -- ${cur}) ) 234 | return 0 235 | fi 236 | case "${prev}" in 237 | 238 | *) 239 | COMPREPLY=() 240 | ;; 241 | esac 242 | COMPREPLY=( $(compgen -W "${opts}" -- ${cur}) ) 243 | return 0 244 | ;; 245 | vmail__cli__alias__show) 246 | opts=" -h -V --help --version " 247 | if [[ ${cur} == -* || ${COMP_CWORD} -eq 3 ]] ; then 248 | COMPREPLY=( $(compgen -W "${opts}" -- ${cur}) ) 249 | return 0 250 | fi 251 | case "${prev}" in 252 | 253 | *) 254 | COMPREPLY=() 255 | ;; 256 | esac 257 | COMPREPLY=( $(compgen -W "${opts}" -- ${cur}) ) 258 | return 0 259 | ;; 260 | vmail__cli__completions) 261 | opts=" -h -V --help --version " 262 | if [[ ${cur} == -* || ${COMP_CWORD} -eq 2 ]] ; then 263 | COMPREPLY=( $(compgen -W "${opts}" -- ${cur}) ) 264 | return 0 265 | fi 266 | case "${prev}" in 267 | 268 | *) 269 | COMPREPLY=() 270 | ;; 271 | esac 272 | COMPREPLY=( $(compgen -W "${opts}" -- ${cur}) ) 273 | return 0 274 | ;; 275 | vmail__cli__domain) 276 | opts=" -h -V --help --version show add remove help list ls rm delete" 277 | if [[ ${cur} == -* || ${COMP_CWORD} -eq 2 ]] ; then 278 | COMPREPLY=( $(compgen -W "${opts}" -- ${cur}) ) 279 | return 0 280 | fi 281 | case "${prev}" in 282 | 283 | *) 284 | COMPREPLY=() 285 | ;; 286 | esac 287 | COMPREPLY=( $(compgen -W "${opts}" -- ${cur}) ) 288 | return 0 289 | ;; 290 | vmail__cli__domain__add) 291 | opts=" -h -V --help --version " 292 | if [[ ${cur} == -* || ${COMP_CWORD} -eq 3 ]] ; then 293 | COMPREPLY=( $(compgen -W "${opts}" -- ${cur}) ) 294 | return 0 295 | fi 296 | case "${prev}" in 297 | 298 | *) 299 | COMPREPLY=() 300 | ;; 301 | esac 302 | COMPREPLY=( $(compgen -W "${opts}" -- ${cur}) ) 303 | return 0 304 | ;; 305 | vmail__cli__domain__delete) 306 | opts=" -f -v -h -V --force --verbose --help --version " 307 | if [[ ${cur} == -* || ${COMP_CWORD} -eq 3 ]] ; then 308 | COMPREPLY=( $(compgen -W "${opts}" -- ${cur}) ) 309 | return 0 310 | fi 311 | case "${prev}" in 312 | 313 | *) 314 | COMPREPLY=() 315 | ;; 316 | esac 317 | COMPREPLY=( $(compgen -W "${opts}" -- ${cur}) ) 318 | return 0 319 | ;; 320 | vmail__cli__domain__help) 321 | opts=" -h -V --help --version " 322 | if [[ ${cur} == -* || ${COMP_CWORD} -eq 3 ]] ; then 323 | COMPREPLY=( $(compgen -W "${opts}" -- ${cur}) ) 324 | return 0 325 | fi 326 | case "${prev}" in 327 | 328 | *) 329 | COMPREPLY=() 330 | ;; 331 | esac 332 | COMPREPLY=( $(compgen -W "${opts}" -- ${cur}) ) 333 | return 0 334 | ;; 335 | vmail__cli__domain__list) 336 | opts=" -u -a -h -V --with-users --with-aliases --help --version " 337 | if [[ ${cur} == -* || ${COMP_CWORD} -eq 3 ]] ; then 338 | COMPREPLY=( $(compgen -W "${opts}" -- ${cur}) ) 339 | return 0 340 | fi 341 | case "${prev}" in 342 | 343 | *) 344 | COMPREPLY=() 345 | ;; 346 | esac 347 | COMPREPLY=( $(compgen -W "${opts}" -- ${cur}) ) 348 | return 0 349 | ;; 350 | vmail__cli__domain__ls) 351 | opts=" -u -a -h -V --with-users --with-aliases --help --version " 352 | if [[ ${cur} == -* || ${COMP_CWORD} -eq 3 ]] ; then 353 | COMPREPLY=( $(compgen -W "${opts}" -- ${cur}) ) 354 | return 0 355 | fi 356 | case "${prev}" in 357 | 358 | *) 359 | COMPREPLY=() 360 | ;; 361 | esac 362 | COMPREPLY=( $(compgen -W "${opts}" -- ${cur}) ) 363 | return 0 364 | ;; 365 | vmail__cli__domain__remove) 366 | opts=" -f -v -h -V --force --verbose --help --version " 367 | if [[ ${cur} == -* || ${COMP_CWORD} -eq 3 ]] ; then 368 | COMPREPLY=( $(compgen -W "${opts}" -- ${cur}) ) 369 | return 0 370 | fi 371 | case "${prev}" in 372 | 373 | *) 374 | COMPREPLY=() 375 | ;; 376 | esac 377 | COMPREPLY=( $(compgen -W "${opts}" -- ${cur}) ) 378 | return 0 379 | ;; 380 | vmail__cli__domain__rm) 381 | opts=" -f -v -h -V --force --verbose --help --version " 382 | if [[ ${cur} == -* || ${COMP_CWORD} -eq 3 ]] ; then 383 | COMPREPLY=( $(compgen -W "${opts}" -- ${cur}) ) 384 | return 0 385 | fi 386 | case "${prev}" in 387 | 388 | *) 389 | COMPREPLY=() 390 | ;; 391 | esac 392 | COMPREPLY=( $(compgen -W "${opts}" -- ${cur}) ) 393 | return 0 394 | ;; 395 | vmail__cli__domain__show) 396 | opts=" -u -a -h -V --with-users --with-aliases --help --version " 397 | if [[ ${cur} == -* || ${COMP_CWORD} -eq 3 ]] ; then 398 | COMPREPLY=( $(compgen -W "${opts}" -- ${cur}) ) 399 | return 0 400 | fi 401 | case "${prev}" in 402 | 403 | *) 404 | COMPREPLY=() 405 | ;; 406 | esac 407 | COMPREPLY=( $(compgen -W "${opts}" -- ${cur}) ) 408 | return 0 409 | ;; 410 | vmail__cli__help) 411 | opts=" -h -V --help --version " 412 | if [[ ${cur} == -* || ${COMP_CWORD} -eq 2 ]] ; then 413 | COMPREPLY=( $(compgen -W "${opts}" -- ${cur}) ) 414 | return 0 415 | fi 416 | case "${prev}" in 417 | 418 | *) 419 | COMPREPLY=() 420 | ;; 421 | esac 422 | COMPREPLY=( $(compgen -W "${opts}" -- ${cur}) ) 423 | return 0 424 | ;; 425 | vmail__cli__user) 426 | opts=" -h -V -d --help --version --domain show add remove password edit help create new rm delete pw change update" 427 | if [[ ${cur} == -* || ${COMP_CWORD} -eq 2 ]] ; then 428 | COMPREPLY=( $(compgen -W "${opts}" -- ${cur}) ) 429 | return 0 430 | fi 431 | case "${prev}" in 432 | 433 | --domain) 434 | COMPREPLY=($(compgen -f ${cur})) 435 | return 0 436 | ;; 437 | -d) 438 | COMPREPLY=($(compgen -f ${cur})) 439 | return 0 440 | ;; 441 | *) 442 | COMPREPLY=() 443 | ;; 444 | esac 445 | COMPREPLY=( $(compgen -W "${opts}" -- ${cur}) ) 446 | return 0 447 | ;; 448 | vmail__cli__user__add) 449 | opts=" -d -s -h -V -q --disabled --send-only --help --version --quota " 450 | if [[ ${cur} == -* || ${COMP_CWORD} -eq 3 ]] ; then 451 | COMPREPLY=( $(compgen -W "${opts}" -- ${cur}) ) 452 | return 0 453 | fi 454 | case "${prev}" in 455 | 456 | --quota) 457 | COMPREPLY=($(compgen -f ${cur})) 458 | return 0 459 | ;; 460 | -q) 461 | COMPREPLY=($(compgen -f ${cur})) 462 | return 0 463 | ;; 464 | *) 465 | COMPREPLY=() 466 | ;; 467 | esac 468 | COMPREPLY=( $(compgen -W "${opts}" -- ${cur}) ) 469 | return 0 470 | ;; 471 | vmail__cli__user__change) 472 | opts=" -d -e -s -r -h -V -q --disable --enable --send-only --send-receive --help --version --quota " 473 | if [[ ${cur} == -* || ${COMP_CWORD} -eq 3 ]] ; then 474 | COMPREPLY=( $(compgen -W "${opts}" -- ${cur}) ) 475 | return 0 476 | fi 477 | case "${prev}" in 478 | 479 | --quota) 480 | COMPREPLY=($(compgen -f ${cur})) 481 | return 0 482 | ;; 483 | -q) 484 | COMPREPLY=($(compgen -f ${cur})) 485 | return 0 486 | ;; 487 | *) 488 | COMPREPLY=() 489 | ;; 490 | esac 491 | COMPREPLY=( $(compgen -W "${opts}" -- ${cur}) ) 492 | return 0 493 | ;; 494 | vmail__cli__user__create) 495 | opts=" -d -s -h -V -q --disabled --send-only --help --version --quota " 496 | if [[ ${cur} == -* || ${COMP_CWORD} -eq 3 ]] ; then 497 | COMPREPLY=( $(compgen -W "${opts}" -- ${cur}) ) 498 | return 0 499 | fi 500 | case "${prev}" in 501 | 502 | --quota) 503 | COMPREPLY=($(compgen -f ${cur})) 504 | return 0 505 | ;; 506 | -q) 507 | COMPREPLY=($(compgen -f ${cur})) 508 | return 0 509 | ;; 510 | *) 511 | COMPREPLY=() 512 | ;; 513 | esac 514 | COMPREPLY=( $(compgen -W "${opts}" -- ${cur}) ) 515 | return 0 516 | ;; 517 | vmail__cli__user__delete) 518 | opts=" -f -v -h -V --force --verbose --help --version " 519 | if [[ ${cur} == -* || ${COMP_CWORD} -eq 3 ]] ; then 520 | COMPREPLY=( $(compgen -W "${opts}" -- ${cur}) ) 521 | return 0 522 | fi 523 | case "${prev}" in 524 | 525 | *) 526 | COMPREPLY=() 527 | ;; 528 | esac 529 | COMPREPLY=( $(compgen -W "${opts}" -- ${cur}) ) 530 | return 0 531 | ;; 532 | vmail__cli__user__edit) 533 | opts=" -d -e -s -r -h -V -q --disable --enable --send-only --send-receive --help --version --quota " 534 | if [[ ${cur} == -* || ${COMP_CWORD} -eq 3 ]] ; then 535 | COMPREPLY=( $(compgen -W "${opts}" -- ${cur}) ) 536 | return 0 537 | fi 538 | case "${prev}" in 539 | 540 | --quota) 541 | COMPREPLY=($(compgen -f ${cur})) 542 | return 0 543 | ;; 544 | -q) 545 | COMPREPLY=($(compgen -f ${cur})) 546 | return 0 547 | ;; 548 | *) 549 | COMPREPLY=() 550 | ;; 551 | esac 552 | COMPREPLY=( $(compgen -W "${opts}" -- ${cur}) ) 553 | return 0 554 | ;; 555 | vmail__cli__user__help) 556 | opts=" -h -V --help --version " 557 | if [[ ${cur} == -* || ${COMP_CWORD} -eq 3 ]] ; then 558 | COMPREPLY=( $(compgen -W "${opts}" -- ${cur}) ) 559 | return 0 560 | fi 561 | case "${prev}" in 562 | 563 | *) 564 | COMPREPLY=() 565 | ;; 566 | esac 567 | COMPREPLY=( $(compgen -W "${opts}" -- ${cur}) ) 568 | return 0 569 | ;; 570 | vmail__cli__user__new) 571 | opts=" -d -s -h -V -q --disabled --send-only --help --version --quota " 572 | if [[ ${cur} == -* || ${COMP_CWORD} -eq 3 ]] ; then 573 | COMPREPLY=( $(compgen -W "${opts}" -- ${cur}) ) 574 | return 0 575 | fi 576 | case "${prev}" in 577 | 578 | --quota) 579 | COMPREPLY=($(compgen -f ${cur})) 580 | return 0 581 | ;; 582 | -q) 583 | COMPREPLY=($(compgen -f ${cur})) 584 | return 0 585 | ;; 586 | *) 587 | COMPREPLY=() 588 | ;; 589 | esac 590 | COMPREPLY=( $(compgen -W "${opts}" -- ${cur}) ) 591 | return 0 592 | ;; 593 | vmail__cli__user__password) 594 | opts=" -h -V --help --version " 595 | if [[ ${cur} == -* || ${COMP_CWORD} -eq 3 ]] ; then 596 | COMPREPLY=( $(compgen -W "${opts}" -- ${cur}) ) 597 | return 0 598 | fi 599 | case "${prev}" in 600 | 601 | *) 602 | COMPREPLY=() 603 | ;; 604 | esac 605 | COMPREPLY=( $(compgen -W "${opts}" -- ${cur}) ) 606 | return 0 607 | ;; 608 | vmail__cli__user__pw) 609 | opts=" -h -V --help --version " 610 | if [[ ${cur} == -* || ${COMP_CWORD} -eq 3 ]] ; then 611 | COMPREPLY=( $(compgen -W "${opts}" -- ${cur}) ) 612 | return 0 613 | fi 614 | case "${prev}" in 615 | 616 | *) 617 | COMPREPLY=() 618 | ;; 619 | esac 620 | COMPREPLY=( $(compgen -W "${opts}" -- ${cur}) ) 621 | return 0 622 | ;; 623 | vmail__cli__user__remove) 624 | opts=" -f -v -h -V --force --verbose --help --version " 625 | if [[ ${cur} == -* || ${COMP_CWORD} -eq 3 ]] ; then 626 | COMPREPLY=( $(compgen -W "${opts}" -- ${cur}) ) 627 | return 0 628 | fi 629 | case "${prev}" in 630 | 631 | *) 632 | COMPREPLY=() 633 | ;; 634 | esac 635 | COMPREPLY=( $(compgen -W "${opts}" -- ${cur}) ) 636 | return 0 637 | ;; 638 | vmail__cli__user__rm) 639 | opts=" -f -v -h -V --force --verbose --help --version " 640 | if [[ ${cur} == -* || ${COMP_CWORD} -eq 3 ]] ; then 641 | COMPREPLY=( $(compgen -W "${opts}" -- ${cur}) ) 642 | return 0 643 | fi 644 | case "${prev}" in 645 | 646 | *) 647 | COMPREPLY=() 648 | ;; 649 | esac 650 | COMPREPLY=( $(compgen -W "${opts}" -- ${cur}) ) 651 | return 0 652 | ;; 653 | vmail__cli__user__show) 654 | opts=" -v -h -V --verbose --help --version " 655 | if [[ ${cur} == -* || ${COMP_CWORD} -eq 3 ]] ; then 656 | COMPREPLY=( $(compgen -W "${opts}" -- ${cur}) ) 657 | return 0 658 | fi 659 | case "${prev}" in 660 | 661 | *) 662 | COMPREPLY=() 663 | ;; 664 | esac 665 | COMPREPLY=( $(compgen -W "${opts}" -- ${cur}) ) 666 | return 0 667 | ;; 668 | vmail__cli__user__update) 669 | opts=" -d -e -s -r -h -V -q --disable --enable --send-only --send-receive --help --version --quota " 670 | if [[ ${cur} == -* || ${COMP_CWORD} -eq 3 ]] ; then 671 | COMPREPLY=( $(compgen -W "${opts}" -- ${cur}) ) 672 | return 0 673 | fi 674 | case "${prev}" in 675 | 676 | --quota) 677 | COMPREPLY=($(compgen -f ${cur})) 678 | return 0 679 | ;; 680 | -q) 681 | COMPREPLY=($(compgen -f ${cur})) 682 | return 0 683 | ;; 684 | *) 685 | COMPREPLY=() 686 | ;; 687 | esac 688 | COMPREPLY=( $(compgen -W "${opts}" -- ${cur}) ) 689 | return 0 690 | ;; 691 | esac 692 | } 693 | 694 | complete -F _vmail-cli -o bashdefault -o default vmail-cli 695 | -------------------------------------------------------------------------------- /shell/_vmail-cli: -------------------------------------------------------------------------------- 1 | #compdef vmail-cli 2 | 3 | autoload -U is-at-least 4 | 5 | _vmail-cli() { 6 | typeset -A opt_args 7 | typeset -a _arguments_options 8 | local ret=1 9 | 10 | if is-at-least 5.2; then 11 | _arguments_options=(-s -S -C) 12 | else 13 | _arguments_options=(-s -C) 14 | fi 15 | 16 | local context curcontext="$curcontext" state line 17 | _arguments "${_arguments_options[@]}" \ 18 | '-h[Prints help information]' \ 19 | '--help[Prints help information]' \ 20 | '-V[Prints version information]' \ 21 | '--version[Prints version information]' \ 22 | ":: :_vmail-cli_commands" \ 23 | "*::: :->vmail-cli" \ 24 | && ret=0 25 | case $state in 26 | (vmail-cli) 27 | words=($line[1] "${words[@]}") 28 | (( CURRENT += 1 )) 29 | curcontext="${curcontext%:*:*}:vmail-cli-command-$line[1]:" 30 | case $line[1] in 31 | (user) 32 | _arguments "${_arguments_options[@]}" \ 33 | '-d+[Domain to use for the user subcommands, default can be specified in '\''.env'\'' file. E.g. mydomain.tld]' \ 34 | '--domain=[Domain to use for the user subcommands, default can be specified in '\''.env'\'' file. E.g. mydomain.tld]' \ 35 | '-h[Prints help information]' \ 36 | '--help[Prints help information]' \ 37 | '-V[Prints version information]' \ 38 | '--version[Prints version information]' \ 39 | ":: :_vmail-cli__user_commands" \ 40 | "*::: :->user" \ 41 | && ret=0 42 | case $state in 43 | (user) 44 | words=($line[1] "${words[@]}") 45 | (( CURRENT += 1 )) 46 | curcontext="${curcontext%:*:*}:vmail-cli-user-command-$line[1]:" 47 | case $line[1] in 48 | (show) 49 | _arguments "${_arguments_options[@]}" \ 50 | '-v[Verbose output (include aliases)]' \ 51 | '--verbose[Verbose output (include aliases)]' \ 52 | '-h[Prints help information]' \ 53 | '--help[Prints help information]' \ 54 | '-V[Prints version information]' \ 55 | '--version[Prints version information]' \ 56 | '::USER -- Name of the user account which should be shown:_files' \ 57 | && ret=0 58 | ;; 59 | (create) 60 | _arguments "${_arguments_options[@]}" \ 61 | '-q+[Quota for user account in MB (Megabyte), default is 0 which is unlimited]' \ 62 | '--quota=[Quota for user account in MB (Megabyte), default is 0 which is unlimited]' \ 63 | '-d[Disable the user, just add it to the database]' \ 64 | '--disabled[Disable the user, just add it to the database]' \ 65 | '-s[Allow the new user only to send email but not receive any.]' \ 66 | '--send-only[Allow the new user only to send email but not receive any.]' \ 67 | '-h[Prints help information]' \ 68 | '--help[Prints help information]' \ 69 | '-V[Prints version information]' \ 70 | '--version[Prints version information]' \ 71 | ':USER -- Name of the user which should be added, without domain name, e.g. 'newuser1':_files' \ 72 | && ret=0 73 | ;; 74 | (new) 75 | _arguments "${_arguments_options[@]}" \ 76 | '-q+[Quota for user account in MB (Megabyte), default is 0 which is unlimited]' \ 77 | '--quota=[Quota for user account in MB (Megabyte), default is 0 which is unlimited]' \ 78 | '-d[Disable the user, just add it to the database]' \ 79 | '--disabled[Disable the user, just add it to the database]' \ 80 | '-s[Allow the new user only to send email but not receive any.]' \ 81 | '--send-only[Allow the new user only to send email but not receive any.]' \ 82 | '-h[Prints help information]' \ 83 | '--help[Prints help information]' \ 84 | '-V[Prints version information]' \ 85 | '--version[Prints version information]' \ 86 | ':USER -- Name of the user which should be added, without domain name, e.g. 'newuser1':_files' \ 87 | && ret=0 88 | ;; 89 | (add) 90 | _arguments "${_arguments_options[@]}" \ 91 | '-q+[Quota for user account in MB (Megabyte), default is 0 which is unlimited]' \ 92 | '--quota=[Quota for user account in MB (Megabyte), default is 0 which is unlimited]' \ 93 | '-d[Disable the user, just add it to the database]' \ 94 | '--disabled[Disable the user, just add it to the database]' \ 95 | '-s[Allow the new user only to send email but not receive any.]' \ 96 | '--send-only[Allow the new user only to send email but not receive any.]' \ 97 | '-h[Prints help information]' \ 98 | '--help[Prints help information]' \ 99 | '-V[Prints version information]' \ 100 | '--version[Prints version information]' \ 101 | ':USER -- Name of the user which should be added, without domain name, e.g. 'newuser1':_files' \ 102 | && ret=0 103 | ;; 104 | (rm) 105 | _arguments "${_arguments_options[@]}" \ 106 | '-f[Force the deleting the given user]' \ 107 | '--force[Force the deleting the given user]' \ 108 | '-v[Verbose output what has been deleted]' \ 109 | '--verbose[Verbose output what has been deleted]' \ 110 | '-h[Prints help information]' \ 111 | '--help[Prints help information]' \ 112 | '-V[Prints version information]' \ 113 | '--version[Prints version information]' \ 114 | ':USER -- User which should be removed:_files' \ 115 | && ret=0 116 | ;; 117 | (delete) 118 | _arguments "${_arguments_options[@]}" \ 119 | '-f[Force the deleting the given user]' \ 120 | '--force[Force the deleting the given user]' \ 121 | '-v[Verbose output what has been deleted]' \ 122 | '--verbose[Verbose output what has been deleted]' \ 123 | '-h[Prints help information]' \ 124 | '--help[Prints help information]' \ 125 | '-V[Prints version information]' \ 126 | '--version[Prints version information]' \ 127 | ':USER -- User which should be removed:_files' \ 128 | && ret=0 129 | ;; 130 | (remove) 131 | _arguments "${_arguments_options[@]}" \ 132 | '-f[Force the deleting the given user]' \ 133 | '--force[Force the deleting the given user]' \ 134 | '-v[Verbose output what has been deleted]' \ 135 | '--verbose[Verbose output what has been deleted]' \ 136 | '-h[Prints help information]' \ 137 | '--help[Prints help information]' \ 138 | '-V[Prints version information]' \ 139 | '--version[Prints version information]' \ 140 | ':USER -- User which should be removed:_files' \ 141 | && ret=0 142 | ;; 143 | (pw) 144 | _arguments "${_arguments_options[@]}" \ 145 | '-h[Prints help information]' \ 146 | '--help[Prints help information]' \ 147 | '-V[Prints version information]' \ 148 | '--version[Prints version information]' \ 149 | ':USER -- The user name which should be edited:_files' \ 150 | && ret=0 151 | ;; 152 | (password) 153 | _arguments "${_arguments_options[@]}" \ 154 | '-h[Prints help information]' \ 155 | '--help[Prints help information]' \ 156 | '-V[Prints version information]' \ 157 | '--version[Prints version information]' \ 158 | ':USER -- The user name which should be edited:_files' \ 159 | && ret=0 160 | ;; 161 | (change) 162 | _arguments "${_arguments_options[@]}" \ 163 | '-q+[Quota for user account in MB (Megabyte), 0 is unlimited]' \ 164 | '--quota=[Quota for user account in MB (Megabyte), 0 is unlimited]' \ 165 | '(-e --enable)-d[Disable given user]' \ 166 | '(-e --enable)--disable[Disable given user]' \ 167 | '(-d --disable)-e[Enable given user]' \ 168 | '(-d --disable)--enable[Enable given user]' \ 169 | '(-r --send-receive)-s[Allow user only to send]' \ 170 | '(-r --send-receive)--send-only[Allow user only to send]' \ 171 | '(-s --send-only)-r[Allow user to send and receive]' \ 172 | '(-s --send-only)--send-receive[Allow user to send and receive]' \ 173 | '-h[Prints help information]' \ 174 | '--help[Prints help information]' \ 175 | '-V[Prints version information]' \ 176 | '--version[Prints version information]' \ 177 | ':USER -- The user name which should be edited:_files' \ 178 | && ret=0 179 | ;; 180 | (update) 181 | _arguments "${_arguments_options[@]}" \ 182 | '-q+[Quota for user account in MB (Megabyte), 0 is unlimited]' \ 183 | '--quota=[Quota for user account in MB (Megabyte), 0 is unlimited]' \ 184 | '(-e --enable)-d[Disable given user]' \ 185 | '(-e --enable)--disable[Disable given user]' \ 186 | '(-d --disable)-e[Enable given user]' \ 187 | '(-d --disable)--enable[Enable given user]' \ 188 | '(-r --send-receive)-s[Allow user only to send]' \ 189 | '(-r --send-receive)--send-only[Allow user only to send]' \ 190 | '(-s --send-only)-r[Allow user to send and receive]' \ 191 | '(-s --send-only)--send-receive[Allow user to send and receive]' \ 192 | '-h[Prints help information]' \ 193 | '--help[Prints help information]' \ 194 | '-V[Prints version information]' \ 195 | '--version[Prints version information]' \ 196 | ':USER -- The user name which should be edited:_files' \ 197 | && ret=0 198 | ;; 199 | (edit) 200 | _arguments "${_arguments_options[@]}" \ 201 | '-q+[Quota for user account in MB (Megabyte), 0 is unlimited]' \ 202 | '--quota=[Quota for user account in MB (Megabyte), 0 is unlimited]' \ 203 | '(-e --enable)-d[Disable given user]' \ 204 | '(-e --enable)--disable[Disable given user]' \ 205 | '(-d --disable)-e[Enable given user]' \ 206 | '(-d --disable)--enable[Enable given user]' \ 207 | '(-r --send-receive)-s[Allow user only to send]' \ 208 | '(-r --send-receive)--send-only[Allow user only to send]' \ 209 | '(-s --send-only)-r[Allow user to send and receive]' \ 210 | '(-s --send-only)--send-receive[Allow user to send and receive]' \ 211 | '-h[Prints help information]' \ 212 | '--help[Prints help information]' \ 213 | '-V[Prints version information]' \ 214 | '--version[Prints version information]' \ 215 | ':USER -- The user name which should be edited:_files' \ 216 | && ret=0 217 | ;; 218 | (help) 219 | _arguments "${_arguments_options[@]}" \ 220 | '-h[Prints help information]' \ 221 | '--help[Prints help information]' \ 222 | '-V[Prints version information]' \ 223 | '--version[Prints version information]' \ 224 | && ret=0 225 | ;; 226 | esac 227 | ;; 228 | esac 229 | ;; 230 | (domain) 231 | _arguments "${_arguments_options[@]}" \ 232 | '-h[Prints help information]' \ 233 | '--help[Prints help information]' \ 234 | '-V[Prints version information]' \ 235 | '--version[Prints version information]' \ 236 | ":: :_vmail-cli__domain_commands" \ 237 | "*::: :->domain" \ 238 | && ret=0 239 | case $state in 240 | (domain) 241 | words=($line[1] "${words[@]}") 242 | (( CURRENT += 1 )) 243 | curcontext="${curcontext%:*:*}:vmail-cli-domain-command-$line[1]:" 244 | case $line[1] in 245 | (list) 246 | _arguments "${_arguments_options[@]}" \ 247 | '-u[Show all users for the domain]' \ 248 | '--with-users[Show all users for the domain]' \ 249 | '-a[Show all aliases for the users]' \ 250 | '--with-aliases[Show all aliases for the users]' \ 251 | '-h[Prints help information]' \ 252 | '--help[Prints help information]' \ 253 | '-V[Prints version information]' \ 254 | '--version[Prints version information]' \ 255 | '::DOMAIN -- Filter on domain:_files' \ 256 | && ret=0 257 | ;; 258 | (ls) 259 | _arguments "${_arguments_options[@]}" \ 260 | '-u[Show all users for the domain]' \ 261 | '--with-users[Show all users for the domain]' \ 262 | '-a[Show all aliases for the users]' \ 263 | '--with-aliases[Show all aliases for the users]' \ 264 | '-h[Prints help information]' \ 265 | '--help[Prints help information]' \ 266 | '-V[Prints version information]' \ 267 | '--version[Prints version information]' \ 268 | '::DOMAIN -- Filter on domain:_files' \ 269 | && ret=0 270 | ;; 271 | (show) 272 | _arguments "${_arguments_options[@]}" \ 273 | '-u[Show all users for the domain]' \ 274 | '--with-users[Show all users for the domain]' \ 275 | '-a[Show all aliases for the users]' \ 276 | '--with-aliases[Show all aliases for the users]' \ 277 | '-h[Prints help information]' \ 278 | '--help[Prints help information]' \ 279 | '-V[Prints version information]' \ 280 | '--version[Prints version information]' \ 281 | '::DOMAIN -- Filter on domain:_files' \ 282 | && ret=0 283 | ;; 284 | (add) 285 | _arguments "${_arguments_options[@]}" \ 286 | '-h[Prints help information]' \ 287 | '--help[Prints help information]' \ 288 | '-V[Prints version information]' \ 289 | '--version[Prints version information]' \ 290 | ':DOMAIN -- The domain name which should be added.:_files' \ 291 | && ret=0 292 | ;; 293 | (rm) 294 | _arguments "${_arguments_options[@]}" \ 295 | '-f[Force the deleting the given domain]' \ 296 | '--force[Force the deleting the given domain]' \ 297 | '-v[Verbose output what has been deleted]' \ 298 | '--verbose[Verbose output what has been deleted]' \ 299 | '-h[Prints help information]' \ 300 | '--help[Prints help information]' \ 301 | '-V[Prints version information]' \ 302 | '--version[Prints version information]' \ 303 | ':DOMAIN -- The domain name which should be deleted.:_files' \ 304 | && ret=0 305 | ;; 306 | (delete) 307 | _arguments "${_arguments_options[@]}" \ 308 | '-f[Force the deleting the given domain]' \ 309 | '--force[Force the deleting the given domain]' \ 310 | '-v[Verbose output what has been deleted]' \ 311 | '--verbose[Verbose output what has been deleted]' \ 312 | '-h[Prints help information]' \ 313 | '--help[Prints help information]' \ 314 | '-V[Prints version information]' \ 315 | '--version[Prints version information]' \ 316 | ':DOMAIN -- The domain name which should be deleted.:_files' \ 317 | && ret=0 318 | ;; 319 | (remove) 320 | _arguments "${_arguments_options[@]}" \ 321 | '-f[Force the deleting the given domain]' \ 322 | '--force[Force the deleting the given domain]' \ 323 | '-v[Verbose output what has been deleted]' \ 324 | '--verbose[Verbose output what has been deleted]' \ 325 | '-h[Prints help information]' \ 326 | '--help[Prints help information]' \ 327 | '-V[Prints version information]' \ 328 | '--version[Prints version information]' \ 329 | ':DOMAIN -- The domain name which should be deleted.:_files' \ 330 | && ret=0 331 | ;; 332 | (help) 333 | _arguments "${_arguments_options[@]}" \ 334 | '-h[Prints help information]' \ 335 | '--help[Prints help information]' \ 336 | '-V[Prints version information]' \ 337 | '--version[Prints version information]' \ 338 | && ret=0 339 | ;; 340 | esac 341 | ;; 342 | esac 343 | ;; 344 | (alias) 345 | _arguments "${_arguments_options[@]}" \ 346 | '-h[Prints help information]' \ 347 | '--help[Prints help information]' \ 348 | '-V[Prints version information]' \ 349 | '--version[Prints version information]' \ 350 | ":: :_vmail-cli__alias_commands" \ 351 | "*::: :->alias" \ 352 | && ret=0 353 | case $state in 354 | (alias) 355 | words=($line[1] "${words[@]}") 356 | (( CURRENT += 1 )) 357 | curcontext="${curcontext%:*:*}:vmail-cli-alias-command-$line[1]:" 358 | case $line[1] in 359 | (list) 360 | _arguments "${_arguments_options[@]}" \ 361 | '-h[Prints help information]' \ 362 | '--help[Prints help information]' \ 363 | '-V[Prints version information]' \ 364 | '--version[Prints version information]' \ 365 | '::DEST_USER -- username to filter for:_files' \ 366 | '::DEST_DOMAIN -- domain to filter for:_files' \ 367 | && ret=0 368 | ;; 369 | (ls) 370 | _arguments "${_arguments_options[@]}" \ 371 | '-h[Prints help information]' \ 372 | '--help[Prints help information]' \ 373 | '-V[Prints version information]' \ 374 | '--version[Prints version information]' \ 375 | '::DEST_USER -- username to filter for:_files' \ 376 | '::DEST_DOMAIN -- domain to filter for:_files' \ 377 | && ret=0 378 | ;; 379 | (show) 380 | _arguments "${_arguments_options[@]}" \ 381 | '-h[Prints help information]' \ 382 | '--help[Prints help information]' \ 383 | '-V[Prints version information]' \ 384 | '--version[Prints version information]' \ 385 | '::DEST_USER -- username to filter for:_files' \ 386 | '::DEST_DOMAIN -- domain to filter for:_files' \ 387 | && ret=0 388 | ;; 389 | (create) 390 | _arguments "${_arguments_options[@]}" \ 391 | '-d[Set alias to disabled]' \ 392 | '--disabled[Set alias to disabled]' \ 393 | '-h[Prints help information]' \ 394 | '--help[Prints help information]' \ 395 | '-V[Prints version information]' \ 396 | '--version[Prints version information]' \ 397 | ':USER -- The username for the alias:_files' \ 398 | ':DOMAIN -- Existing domain for the alias:_files' \ 399 | ':DEST_USER -- Existing user account:_files' \ 400 | '::DEST_DOMAIN -- If not specified, this will assume to DOMAIN value:_files' \ 401 | && ret=0 402 | ;; 403 | (new) 404 | _arguments "${_arguments_options[@]}" \ 405 | '-d[Set alias to disabled]' \ 406 | '--disabled[Set alias to disabled]' \ 407 | '-h[Prints help information]' \ 408 | '--help[Prints help information]' \ 409 | '-V[Prints version information]' \ 410 | '--version[Prints version information]' \ 411 | ':USER -- The username for the alias:_files' \ 412 | ':DOMAIN -- Existing domain for the alias:_files' \ 413 | ':DEST_USER -- Existing user account:_files' \ 414 | '::DEST_DOMAIN -- If not specified, this will assume to DOMAIN value:_files' \ 415 | && ret=0 416 | ;; 417 | (add) 418 | _arguments "${_arguments_options[@]}" \ 419 | '-d[Set alias to disabled]' \ 420 | '--disabled[Set alias to disabled]' \ 421 | '-h[Prints help information]' \ 422 | '--help[Prints help information]' \ 423 | '-V[Prints version information]' \ 424 | '--version[Prints version information]' \ 425 | ':USER -- The username for the alias:_files' \ 426 | ':DOMAIN -- Existing domain for the alias:_files' \ 427 | ':DEST_USER -- Existing user account:_files' \ 428 | '::DEST_DOMAIN -- If not specified, this will assume to DOMAIN value:_files' \ 429 | && ret=0 430 | ;; 431 | (rm) 432 | _arguments "${_arguments_options[@]}" \ 433 | '-h[Prints help information]' \ 434 | '--help[Prints help information]' \ 435 | '-V[Prints version information]' \ 436 | '--version[Prints version information]' \ 437 | ':USER -- :_files' \ 438 | ':DOMAIN -- Existing domain:_files' \ 439 | && ret=0 440 | ;; 441 | (delete) 442 | _arguments "${_arguments_options[@]}" \ 443 | '-h[Prints help information]' \ 444 | '--help[Prints help information]' \ 445 | '-V[Prints version information]' \ 446 | '--version[Prints version information]' \ 447 | ':USER -- :_files' \ 448 | ':DOMAIN -- Existing domain:_files' \ 449 | && ret=0 450 | ;; 451 | (remove) 452 | _arguments "${_arguments_options[@]}" \ 453 | '-h[Prints help information]' \ 454 | '--help[Prints help information]' \ 455 | '-V[Prints version information]' \ 456 | '--version[Prints version information]' \ 457 | ':USER -- :_files' \ 458 | ':DOMAIN -- Existing domain:_files' \ 459 | && ret=0 460 | ;; 461 | (help) 462 | _arguments "${_arguments_options[@]}" \ 463 | '-h[Prints help information]' \ 464 | '--help[Prints help information]' \ 465 | '-V[Prints version information]' \ 466 | '--version[Prints version information]' \ 467 | && ret=0 468 | ;; 469 | esac 470 | ;; 471 | esac 472 | ;; 473 | (completions) 474 | _arguments "${_arguments_options[@]}" \ 475 | '-h[Prints help information]' \ 476 | '--help[Prints help information]' \ 477 | '-V[Prints version information]' \ 478 | '--version[Prints version information]' \ 479 | ':SHELL -- The shell to generate the script for:(bash fish zsh)' \ 480 | && ret=0 481 | ;; 482 | (help) 483 | _arguments "${_arguments_options[@]}" \ 484 | '-h[Prints help information]' \ 485 | '--help[Prints help information]' \ 486 | '-V[Prints version information]' \ 487 | '--version[Prints version information]' \ 488 | && ret=0 489 | ;; 490 | esac 491 | ;; 492 | esac 493 | } 494 | 495 | (( $+functions[_vmail-cli_commands] )) || 496 | _vmail-cli_commands() { 497 | local commands; commands=( 498 | "user:User management for the vmail database" \ 499 | "domain:Manage domains for a vmail database" \ 500 | "alias:Manage aliases for the vmail database" \ 501 | "completions:Generates completion scripts for your shell" \ 502 | "help:Prints this message or the help of the given subcommand(s)" \ 503 | ) 504 | _describe -t commands 'vmail-cli commands' commands "$@" 505 | } 506 | (( $+functions[_vmail-cli__alias__add_commands] )) || 507 | _vmail-cli__alias__add_commands() { 508 | local commands; commands=( 509 | 510 | ) 511 | _describe -t commands 'vmail-cli alias add commands' commands "$@" 512 | } 513 | (( $+functions[_vmail-cli__domain__add_commands] )) || 514 | _vmail-cli__domain__add_commands() { 515 | local commands; commands=( 516 | 517 | ) 518 | _describe -t commands 'vmail-cli domain add commands' commands "$@" 519 | } 520 | (( $+functions[_vmail-cli__user__add_commands] )) || 521 | _vmail-cli__user__add_commands() { 522 | local commands; commands=( 523 | 524 | ) 525 | _describe -t commands 'vmail-cli user add commands' commands "$@" 526 | } 527 | (( $+functions[_vmail-cli__alias_commands] )) || 528 | _vmail-cli__alias_commands() { 529 | local commands; commands=( 530 | "show:Show aliases for user" \ 531 | "add:Add an alias to an existing user account" \ 532 | "remove:Remove an alias from the database" \ 533 | "help:Prints this message or the help of the given subcommand(s)" \ 534 | ) 535 | _describe -t commands 'vmail-cli alias commands' commands "$@" 536 | } 537 | (( $+functions[_vmail-cli__change_commands] )) || 538 | _vmail-cli__change_commands() { 539 | local commands; commands=( 540 | 541 | ) 542 | _describe -t commands 'vmail-cli change commands' commands "$@" 543 | } 544 | (( $+functions[_vmail-cli__user__change_commands] )) || 545 | _vmail-cli__user__change_commands() { 546 | local commands; commands=( 547 | 548 | ) 549 | _describe -t commands 'vmail-cli user change commands' commands "$@" 550 | } 551 | (( $+functions[_vmail-cli__completions_commands] )) || 552 | _vmail-cli__completions_commands() { 553 | local commands; commands=( 554 | 555 | ) 556 | _describe -t commands 'vmail-cli completions commands' commands "$@" 557 | } 558 | (( $+functions[_vmail-cli__alias__create_commands] )) || 559 | _vmail-cli__alias__create_commands() { 560 | local commands; commands=( 561 | 562 | ) 563 | _describe -t commands 'vmail-cli alias create commands' commands "$@" 564 | } 565 | (( $+functions[_vmail-cli__create_commands] )) || 566 | _vmail-cli__create_commands() { 567 | local commands; commands=( 568 | 569 | ) 570 | _describe -t commands 'vmail-cli create commands' commands "$@" 571 | } 572 | (( $+functions[_vmail-cli__user__create_commands] )) || 573 | _vmail-cli__user__create_commands() { 574 | local commands; commands=( 575 | 576 | ) 577 | _describe -t commands 'vmail-cli user create commands' commands "$@" 578 | } 579 | (( $+functions[_vmail-cli__alias__delete_commands] )) || 580 | _vmail-cli__alias__delete_commands() { 581 | local commands; commands=( 582 | 583 | ) 584 | _describe -t commands 'vmail-cli alias delete commands' commands "$@" 585 | } 586 | (( $+functions[_vmail-cli__delete_commands] )) || 587 | _vmail-cli__delete_commands() { 588 | local commands; commands=( 589 | 590 | ) 591 | _describe -t commands 'vmail-cli delete commands' commands "$@" 592 | } 593 | (( $+functions[_vmail-cli__domain__delete_commands] )) || 594 | _vmail-cli__domain__delete_commands() { 595 | local commands; commands=( 596 | 597 | ) 598 | _describe -t commands 'vmail-cli domain delete commands' commands "$@" 599 | } 600 | (( $+functions[_vmail-cli__user__delete_commands] )) || 601 | _vmail-cli__user__delete_commands() { 602 | local commands; commands=( 603 | 604 | ) 605 | _describe -t commands 'vmail-cli user delete commands' commands "$@" 606 | } 607 | (( $+functions[_vmail-cli__domain_commands] )) || 608 | _vmail-cli__domain_commands() { 609 | local commands; commands=( 610 | "show:Show domains" \ 611 | "add:Add a new domain to the database" \ 612 | "remove:Remove a domain from the database, this will also delete all related users." \ 613 | "help:Prints this message or the help of the given subcommand(s)" \ 614 | ) 615 | _describe -t commands 'vmail-cli domain commands' commands "$@" 616 | } 617 | (( $+functions[_vmail-cli__user__edit_commands] )) || 618 | _vmail-cli__user__edit_commands() { 619 | local commands; commands=( 620 | 621 | ) 622 | _describe -t commands 'vmail-cli user edit commands' commands "$@" 623 | } 624 | (( $+functions[_vmail-cli__alias__help_commands] )) || 625 | _vmail-cli__alias__help_commands() { 626 | local commands; commands=( 627 | 628 | ) 629 | _describe -t commands 'vmail-cli alias help commands' commands "$@" 630 | } 631 | (( $+functions[_vmail-cli__domain__help_commands] )) || 632 | _vmail-cli__domain__help_commands() { 633 | local commands; commands=( 634 | 635 | ) 636 | _describe -t commands 'vmail-cli domain help commands' commands "$@" 637 | } 638 | (( $+functions[_vmail-cli__help_commands] )) || 639 | _vmail-cli__help_commands() { 640 | local commands; commands=( 641 | 642 | ) 643 | _describe -t commands 'vmail-cli help commands' commands "$@" 644 | } 645 | (( $+functions[_vmail-cli__user__help_commands] )) || 646 | _vmail-cli__user__help_commands() { 647 | local commands; commands=( 648 | 649 | ) 650 | _describe -t commands 'vmail-cli user help commands' commands "$@" 651 | } 652 | (( $+functions[_vmail-cli__alias__list_commands] )) || 653 | _vmail-cli__alias__list_commands() { 654 | local commands; commands=( 655 | 656 | ) 657 | _describe -t commands 'vmail-cli alias list commands' commands "$@" 658 | } 659 | (( $+functions[_vmail-cli__domain__list_commands] )) || 660 | _vmail-cli__domain__list_commands() { 661 | local commands; commands=( 662 | 663 | ) 664 | _describe -t commands 'vmail-cli domain list commands' commands "$@" 665 | } 666 | (( $+functions[_vmail-cli__list_commands] )) || 667 | _vmail-cli__list_commands() { 668 | local commands; commands=( 669 | 670 | ) 671 | _describe -t commands 'vmail-cli list commands' commands "$@" 672 | } 673 | (( $+functions[_vmail-cli__alias__ls_commands] )) || 674 | _vmail-cli__alias__ls_commands() { 675 | local commands; commands=( 676 | 677 | ) 678 | _describe -t commands 'vmail-cli alias ls commands' commands "$@" 679 | } 680 | (( $+functions[_vmail-cli__domain__ls_commands] )) || 681 | _vmail-cli__domain__ls_commands() { 682 | local commands; commands=( 683 | 684 | ) 685 | _describe -t commands 'vmail-cli domain ls commands' commands "$@" 686 | } 687 | (( $+functions[_vmail-cli__ls_commands] )) || 688 | _vmail-cli__ls_commands() { 689 | local commands; commands=( 690 | 691 | ) 692 | _describe -t commands 'vmail-cli ls commands' commands "$@" 693 | } 694 | (( $+functions[_vmail-cli__alias__new_commands] )) || 695 | _vmail-cli__alias__new_commands() { 696 | local commands; commands=( 697 | 698 | ) 699 | _describe -t commands 'vmail-cli alias new commands' commands "$@" 700 | } 701 | (( $+functions[_vmail-cli__new_commands] )) || 702 | _vmail-cli__new_commands() { 703 | local commands; commands=( 704 | 705 | ) 706 | _describe -t commands 'vmail-cli new commands' commands "$@" 707 | } 708 | (( $+functions[_vmail-cli__user__new_commands] )) || 709 | _vmail-cli__user__new_commands() { 710 | local commands; commands=( 711 | 712 | ) 713 | _describe -t commands 'vmail-cli user new commands' commands "$@" 714 | } 715 | (( $+functions[_vmail-cli__user__password_commands] )) || 716 | _vmail-cli__user__password_commands() { 717 | local commands; commands=( 718 | 719 | ) 720 | _describe -t commands 'vmail-cli user password commands' commands "$@" 721 | } 722 | (( $+functions[_vmail-cli__pw_commands] )) || 723 | _vmail-cli__pw_commands() { 724 | local commands; commands=( 725 | 726 | ) 727 | _describe -t commands 'vmail-cli pw commands' commands "$@" 728 | } 729 | (( $+functions[_vmail-cli__user__pw_commands] )) || 730 | _vmail-cli__user__pw_commands() { 731 | local commands; commands=( 732 | 733 | ) 734 | _describe -t commands 'vmail-cli user pw commands' commands "$@" 735 | } 736 | (( $+functions[_vmail-cli__alias__remove_commands] )) || 737 | _vmail-cli__alias__remove_commands() { 738 | local commands; commands=( 739 | 740 | ) 741 | _describe -t commands 'vmail-cli alias remove commands' commands "$@" 742 | } 743 | (( $+functions[_vmail-cli__domain__remove_commands] )) || 744 | _vmail-cli__domain__remove_commands() { 745 | local commands; commands=( 746 | 747 | ) 748 | _describe -t commands 'vmail-cli domain remove commands' commands "$@" 749 | } 750 | (( $+functions[_vmail-cli__user__remove_commands] )) || 751 | _vmail-cli__user__remove_commands() { 752 | local commands; commands=( 753 | 754 | ) 755 | _describe -t commands 'vmail-cli user remove commands' commands "$@" 756 | } 757 | (( $+functions[_vmail-cli__alias__rm_commands] )) || 758 | _vmail-cli__alias__rm_commands() { 759 | local commands; commands=( 760 | 761 | ) 762 | _describe -t commands 'vmail-cli alias rm commands' commands "$@" 763 | } 764 | (( $+functions[_vmail-cli__domain__rm_commands] )) || 765 | _vmail-cli__domain__rm_commands() { 766 | local commands; commands=( 767 | 768 | ) 769 | _describe -t commands 'vmail-cli domain rm commands' commands "$@" 770 | } 771 | (( $+functions[_vmail-cli__rm_commands] )) || 772 | _vmail-cli__rm_commands() { 773 | local commands; commands=( 774 | 775 | ) 776 | _describe -t commands 'vmail-cli rm commands' commands "$@" 777 | } 778 | (( $+functions[_vmail-cli__user__rm_commands] )) || 779 | _vmail-cli__user__rm_commands() { 780 | local commands; commands=( 781 | 782 | ) 783 | _describe -t commands 'vmail-cli user rm commands' commands "$@" 784 | } 785 | (( $+functions[_vmail-cli__alias__show_commands] )) || 786 | _vmail-cli__alias__show_commands() { 787 | local commands; commands=( 788 | 789 | ) 790 | _describe -t commands 'vmail-cli alias show commands' commands "$@" 791 | } 792 | (( $+functions[_vmail-cli__domain__show_commands] )) || 793 | _vmail-cli__domain__show_commands() { 794 | local commands; commands=( 795 | 796 | ) 797 | _describe -t commands 'vmail-cli domain show commands' commands "$@" 798 | } 799 | (( $+functions[_vmail-cli__user__show_commands] )) || 800 | _vmail-cli__user__show_commands() { 801 | local commands; commands=( 802 | 803 | ) 804 | _describe -t commands 'vmail-cli user show commands' commands "$@" 805 | } 806 | (( $+functions[_vmail-cli__update_commands] )) || 807 | _vmail-cli__update_commands() { 808 | local commands; commands=( 809 | 810 | ) 811 | _describe -t commands 'vmail-cli update commands' commands "$@" 812 | } 813 | (( $+functions[_vmail-cli__user__update_commands] )) || 814 | _vmail-cli__user__update_commands() { 815 | local commands; commands=( 816 | 817 | ) 818 | _describe -t commands 'vmail-cli user update commands' commands "$@" 819 | } 820 | (( $+functions[_vmail-cli__user_commands] )) || 821 | _vmail-cli__user_commands() { 822 | local commands; commands=( 823 | "show:Show user account(s)" \ 824 | "add:Add a user to the database" \ 825 | "remove:Remove a user from the database, will also delete all aliases for the user" \ 826 | "password:Change the password for given user" \ 827 | "edit:Edit a user account entry" \ 828 | "help:Prints this message or the help of the given subcommand(s)" \ 829 | ) 830 | _describe -t commands 'vmail-cli user commands' commands "$@" 831 | } 832 | 833 | _vmail-cli "$@" --------------------------------------------------------------------------------