#!/bin/bash

# Manage DB writes for:
# - DATABASE_ACCOUNTS
# - DATABASE_DOVECOT_MASTERS

# Logic to perform for requested operations handled here:
function _manage_accounts() {
  local ACTION=${1}
  local DATABASE=${2}
  local MAIL_ACCOUNT=${3}
  # Only for ACTION 'create' or 'update':
  local PASSWD=${4}

  _arg_expect_mail_account

  case "${ACTION}" in
    ( 'create' | 'update' )
      # Fail early before requesting password:
      [[ ${ACTION} == 'create' ]] && _account_should_not_exist_yet
      [[ ${ACTION} == 'update' ]] && _account_should_already_exist
      _password_request_if_missing

      local PASSWD_HASH
      PASSWD_HASH=$(doveadm pw -s SHA512-CRYPT -u "${MAIL_ACCOUNT}" -p "${PASSWD}")
      # Early failure above ensures correct operation => Add (create) or Replace (update):
      _db_entry_add_or_replace "${DATABASE}" "${MAIL_ACCOUNT}" "${PASSWD_HASH}"
      ;;

    ( 'delete' )
      _db_entry_remove "${DATABASE}" "${MAIL_ACCOUNT}"
      ;;

    ( * ) # This should not happen if using convenience wrapper methods:
      _exit_with_error "Unsupported Action: '${ACTION}'"
      ;;

  esac
}

# Convenience wrappers:
DATABASE_ACCOUNTS='/tmp/docker-mailserver/postfix-accounts.cf'
function _manage_accounts_create { _manage_accounts 'create' "${DATABASE_ACCOUNTS}" "${@}" ; }
function _manage_accounts_update { _manage_accounts 'update' "${DATABASE_ACCOUNTS}" "${@}" ; }
function _manage_accounts_delete { _manage_accounts 'delete' "${DATABASE_ACCOUNTS}" "${@}" ; }

# Dovecot Master account support can leverage the same management logic:
DATABASE_DOVECOT_MASTERS='/tmp/docker-mailserver/dovecot-masters.cf'
function _manage_accounts_dovecotmaster_create { _manage_accounts 'create' "${DATABASE_DOVECOT_MASTERS}" "${@}" ; }
function _manage_accounts_dovecotmaster_update { _manage_accounts 'update' "${DATABASE_DOVECOT_MASTERS}" "${@}" ; }
function _manage_accounts_dovecotmaster_delete { _manage_accounts 'delete' "${DATABASE_DOVECOT_MASTERS}" "${@}" ; }

#
# Validation Methods
#

# These validation helpers rely on:
# - Exteral vars to be declared prior to calling them (MAIL_ACCOUNT, PASSWD, DATABASE).
# - Calling external method '__usage' as part of error handling.

# Also used by setquota, delquota
function _arg_expect_mail_account() {
  [[ -z ${MAIL_ACCOUNT} ]] && { __usage ; _exit_with_error 'No account specified' ; }

  # Dovecot Master accounts are validated (they are not email addresses):
  [[ ${DATABASE} == "${DATABASE_DOVECOT_MASTERS}" ]] && return 0

  # Account has both local and domain parts:
  [[ ${MAIL_ACCOUNT} =~ .*\@.* ]] || { __usage ; _exit_with_error "'${MAIL_ACCOUNT}' should include the domain (eg: user@example.com)" ; }
}

function _account_should_not_exist_yet() {
  __account_already_exists && _exit_with_error "'${MAIL_ACCOUNT}' already exists"
  if [[ -f ${DATABASE_VIRTUAL} ]] && grep -q "^${MAIL_ACCOUNT}" "${DATABASE_VIRTUAL}"; then
    _exit_with_error "'${MAIL_ACCOUNT}' is already defined as an alias"
  fi
}

# Also used by delmailuser, setquota, delquota
function _account_should_already_exist() {
  ! __account_already_exists && _exit_with_error "'${MAIL_ACCOUNT}' does not exist"
}

function __account_already_exists() {
  local DATABASE=${DATABASE:-"${DATABASE_ACCOUNTS}"}
  _db_has_entry_with_key "${MAIL_ACCOUNT}" "${DATABASE}"
}

# Also used by addsaslpassword
function _password_request_if_missing() {
  if [[ -z ${PASSWD} ]]; then
    read -r -s -p 'Enter Password: ' PASSWD
    echo
    [[ -z ${PASSWD} ]] && _exit_with_error 'Password must not be empty'
  fi
}