#!/bin/bash # # vmail-stack # https://git.stack-source.com/msb/vmail-stack # Copyright (c) 2022 Matthew Saunders Brown # GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) # # vmail include file, used by other vmail bash scripts # constants readonly VMAIL_DIR=/var/vmail readonly MYSQL_CONNECTION_INFO_FILE=$VMAIL_DIR/.my.cnf # switch to required user if [[ $(basename $0) == "vmail-domains-"* ]] || [[ $(basename $0) == "vmail-dovecot-"* ]] || [[ $(basename $0) == "vmail-webmail-"* ]]; then if [[ "$USER" != "root" ]]; then exec sudo -u root $0 "$@" fi elif [[ $(basename $0) == "vmail-dkim-"* ]]; then if [[ "$USER" != "Debian-exim" ]]; then exec sudo -u Debian-exim -g ssl-cert $0 "$@" fi elif [[ $(basename $0) == "vmail-purge-spool.sh" ]]; then if [[ "$USER" != "Debian-exim" ]]; then exec sudo -u Debian-exim $0 "$@" fi else if [[ "$USER" != "vmail" ]]; then exec sudo -u vmail $0 "$@" else # check that MYSQL_CONNECTION_INFO_FILE exists and is readable if [ ! -f "$MYSQL_CONNECTION_INFO_FILE" ]; then echo "ERROR: MySQL connection info file ($MYSQL_CONNECTION_INFO_FILE) does not exist or is not readable." exit 1 fi fi fi # functions # crude but good enough domain name format validation function vmail::validate_domain () { local my_domain=$1 if [[ $my_domain =~ ^(([a-zA-Z0-9](-?[a-zA-Z0-9])*)\.)+[a-zA-Z]{2,}$ ]] ; then return 0 else return 1 fi } # yesno prompt # # Examples: # loop until y or n: if vmail::yesno "Continue?"; then # default y: if vmail::yesno "Continue?" Y; then # default n: if vmail::yesno "Continue?" N; then function vmail::yesno() { local prompt default reply if [ "${2:-}" = "Y" ]; then prompt="Y/n" default=Y elif [ "${2:-}" = "N" ]; then prompt="y/N" default=N else prompt="y/n" default= fi while true; do read -p "$1 [$prompt] " -n 1 -r reply # Default? if [ -z "$reply" ]; then reply=$default fi # Check if the reply is valid case "$reply" in Y*|y*) return 0 ;; N*|n*) return 1 ;; esac done } function vmail:getoptions () { local OPTIND while getopts "ha:b:d:e:f:g:j:cp:q:r:s:tk:gl:m:o:u:vwxy:" opt ; do case "${opt}" in h ) # display help and exit help exit ;; a ) # alias alias=${OPTARG,,} if [[ $alias =~ "@" ]] ; then domain=${alias##*@} if vmail::validate_domain $domain; then alias=${alias%@*} else echo "ERROR: $domain is not a valid domain name." exit 1 fi fi ;; b ) # body - Body for Autoresponder emails body="${OPTARG}" ;; c ) # csv - output in csv format csv="| sed 's/\t/,/g'" ;; d ) # domain name (virtualhost) to act on domain=${OPTARG,,} if ! vmail::validate_domain $domain; then echo "ERROR: $domain is not a valid domain name." exit 1 fi ;; e ) # email address email=${OPTARG,,} if [[ $email =~ "@" ]] ; then mbox=${email%@*} domain=${email##*@} if [ -z $mbox ] ; then echo "ERROR: No local part in $email." exit 1 elif [ -z $domain ] ; then echo "ERROR: No domain in $email." exit 1 elif ! vmail::validate_domain $domain; then echo "ERROR: $domain is not a valid domain name." exit 1 fi else echo "ERROR: $email is not a valid email." exit 1 fi ;; f ) # forward to email address forward=${OPTARG,,} if [[ $forward =~ "@" ]] ; then forward_mbox=${forward%@*} forward_domain=${forward##*@} if [ -z $forward_mbox ] ; then echo "ERROR: No local part in $forward." exit 1 elif [ -z $forward_domain ] ; then echo "ERROR: No domain in $forward." exit 1 elif ! vmail::validate_domain $forward_domain; then echo "ERROR: $forward_domain is not a valid domain name." exit 1 fi else echo "ERROR: $forward is not a valid email." exit 1 fi ;; g ) # glob (wildcard) search glob=${OPTARG} ;; j ) # Filter Junk filter=${OPTARG} ;; k ) # keep keep=${OPTARG} if [[ $keep != "0" ]] && [[ $keep != "1" ]]; then echo "ERROR: Invalid save keep setting: -k $keep." exit 1 fi ;; l ) # limit limit=${OPTARG} ;; m ) # mbox mbox=${OPTARG} ;; p ) # password password=${OPTARG} ;; o ) # mode for autoresponder - must be Vacation or Autoresponder mode=${OPTARG,,} # first force lower case mode=${mode^} # then capitlize first letter if [[ $mode != "Vacation" ]] && [[ $mode != "Autoresponder" ]]; then echo "ERROR: Invalid mode setting: -o $mode. Must be either Vacation or Autoresponder" exit 1 fi ;; q ) # quota quota=${OPTARG} ;; r ) # ratelimit - hourly limit for sending, multiplied by 10 for daily limit ratelimit=${OPTARG} ;; s ) # status - 0 or 1 or 2 status=${OPTARG} if [[ $status != "0" ]] && [[ $status != "1" ]] && [[ $status != "2" ]]; then echo "ERROR: Invalid status setting: -s $status" exit 1 fi ;; t ) # tab - Use tabs instead of tables for output, do not display column headers tab=true ;; u ) # subject - Subject for Autoresponder emails subject="${OPTARG}" ;; n ) # dry-run dryrun=true ;; v ) # verbose verbose=true ;; w ) # write - store data in file write=true ;; x ) # eXecute - don't prompt for confirmation execute=true ;; y ) # DirectorY - Maildir directory/folder inside mailbox directory="${OPTARG}" ;; \? ) echo "Invalid option: $OPTARG" exit 1 ;; : ) echo "Invalid option: $OPTARG requires an argument" exit 1 ;; esac done shift $((OPTIND-1)) }