Maintaining users in a Dovecut user database
In this code:


while [[ -z "$USERNAME" ]]; do
    echo -n "Please enter username: "
    read "USERNAME"
done



What if the variable USERNAME is not empty when this code is reached?
For example, the environment might be dirty by misbehaving scripts,
and USERNAME might already be set to garbage, and the script will not prompt the user.

echo -n is not recommended because it's not portable. Use printf instead.

There's no need to quote USERNAME when reading into it.

The equivalent code, safer and better:

USERNAME=
while [[ -z "$USERNAME" ]]; do
    printf "Please enter username: "
    read USERNAME
done


Do similarly for the other loops too.



This will fail if PASSWORD contains spaces:


USERPASS="$(doveadm pw -p $PASSWORD -s SHA512-CRYPT)"



To make it safe, quote it:

USERPASS="$(doveadm pw -p "$PASSWORD" -s SHA512-CRYPT)"




It would be better to indent the function bodies. For example:

list_users () {
    if [[ -f "$USERDB" ]]; then
        awk -F: '/./'' {print $1} ' "$USERDB"
    else
        echo "User database not exist or have been moved to another directory!"
        exit 1
    fi
}


This is especially helpful in the case of longer functions.



The quotes are strange and confusing in this awk:


        awk -F: '/./'' {print $1} ' "$USERDB"



It would be better simply this way:

        awk -F: '/./ {print $1}' "$USERDB"