#!/bin/bash 

######################################################################################
# This script facilitates easy command line adding of email users
# It also imposes a few sanity checks.
#######################################################################################




#####
# Special Requirement: tolower, a program that converts its input to lowercase
# You can find an implementation of tolower at www.jmaimon.com/misc/tolower
#
# Special Requirement: apg, A Password Generator - google it.
####

#####
# This script is geared to virt hosting.
# As such it strongly attempts to ensure usernames are Distintively Unique
#####

#####
# This script uses the Sendmail for NT practice of maintaining a virt entry for each username
#####

##################################
##################################
#BEGIN CONFIGURABLES		##
##################################
##################################

######
# Edit this to suit your environment
#####
USERS_MAIL_HOME="/home/mail/"
USERS_BASE_UID="2550"
USERS_HIGH_UID="6000"
USERS_MAIL_GID="mailusers"
PATH=${PATH}:/usr/sbin

PASSWD="passwd"
PASSWDARGS="--stdin"

APG="apg"
# Quoting is removed because otherwise execute_cmd has _VERY_WEIRD_ shell quoting issues.
# Try it if you do not believe me.
APGARGS="-M SNl -m 13 -n 1 -E \'\`\"\\\$"

#You might be able to replace with shell scrip on tr(1)
TOLOWER="tolower"

EDITMAP="editmap"
VIRTDB="/etc/mail/virtusertable"
EDITMAP_VIRTDB_ARGS="-q hash ${VIRTDB}"
VIRTDB_ADDRESSES="/etc/mail/virtusertable.addresses"
MAKE_VIRTDB="/usr/sbin/makevirt"

#MUST BE SOLID YES OR NO. DONT BE CUTE
DONT_WRITE="NO"
DONT_CHECK_EMAILS="NO"
DONT_CHECK_LOGIN="NO"
DONT_CHECK_SENDMAIL="NO"
SENDMAIL_LOCAL_NAMES="/etc/mail/local-host-names"
TRAP_SIGNALS="YES"
SIGNALS_TO_TRAP="SIGKILL SIGHUP SIGTRAP SIGINT SIGABRT SIGUSR1 SIGURG SIGTERM SIGILL  SIGQUIT SIGUSR2 SIGWINCH SIGSEGV" 
EXECUTE_MAKEVIRTDB="NO"

DEFAULT_REPORT_EMAIL="YES"
DEFAULT_REPORT_EMAIL_BCC="YES"
DEFAULT_REPORT_EMAILS[0]="postmaster"
DEFAULT_REPORT_EMAILS[1]="root"

#COMPLETET FLUSH INFO BETWEEN USERNAMES?
COMPLETE_FLUSH="NO"
#############################
#############################
#END CONFIGURABLES###########
#############################
#############################

umask 066

if [[ "x${DONT_WRITE}" == "xYES" ]]; then echo "!!!DEFANGED!!!"; fi

function read_user_info(){
	local LOOPS
	local TEMPVAL
	local ARRCOUNT

	read -ep "Enter users login name suffix:[${USER_PARAMS_SUFFIX}] " 
	if [[ "x${REPLY}" != "x" ]]; then USER_PARAMS_SUFFIX="${REPLY}"; fi

	read -ep "Enter users first name:[${USER_PARAMS_FIRST}] " 
	if [[ "x${REPLY}" != "x" ]]; then USER_PARAMS_FIRST="${REPLY}"; fi

	read -ep "Enter users last name:[${USER_PARAMS_LAST}] "  
	if [[ "x${REPLY}" != "x" ]]; then USER_PARAMS_LAST="${REPLY}"; fi

	USER_PARAMS_DESC="MAIL User - ${USER_PARAMS_FIRST} ${USER_PARAMS_LAST}"
	read -ep "Enter users Description:[${USER_PARAMS_DESC}] " 
	if [[ "x${REPLY}" != "x" ]]; then USER_PARAMS_DESC="${REPLY}"; fi
	
	if [[ "x${USER_PARAMS_SUFFIX}" != "x" ]]; then
		TEMPVAL=`echo "${USER_PARAMS_FIRST:0:1}${USER_PARAMS_LAST}.${USER_PARAMS_SUFFIX}" | tolower`
	else
		TEMPVAL=`echo "${USER_PARAMS_FIRST:0:1}${USER_PARAMS_LAST}" | tolower`
	fi	
	if [[ "x${USER_PARAMS_LOGIN}" == "x" ]]; then
		USER_PARAMS_LOGIN="${TEMPVAL}"
	fi
	read -ep "Enter users login name:[${USER_PARAMS_LOGIN}] " 
	if [[ "x${REPLY}" != "x" ]]; then USER_PARAMS_LOGIN="${REPLY}"; fi

	read -ep "Password (Blank for auto generated): " USER_PARAMS_PASSW
	if [[ "x${USER_PARAMS_PASSW}" == "x" ]]; then
		USER_PARAMS_PASS_AUTO="YES"
		gen_user_passwd;
	else
		USER_PARAMS_PASS_AUTO="NO";
	fi 

	read -ep "Enter users primary email address's domain name:[${USER_PARAMS_PRIM}] " 
	if [[ "x${REPLY}" != "x" ]]; then USER_PARAMS_PRIM="${REPLY}"; fi
	USER_PARAMS_EMAILS[0]="${USER_PARAMS_LOGIN}@${USER_PARAMS_PRIM}"

	echo "User's email addreses. Blank ends/continues. Delete deletes entry"
	if [[ "x${USER_PARAMS_PRIM}" != "x" ]]; then
		echo "Using a default hostpart of @${USER_PARAMS_PRIM}"
	fi

	LOOPS=0
	for temp in "${USER_PARAMS_EMAILS[@]}"; do
		if [[ ${LOOPS} == 0 ]]; then 
			unset USER_PARAMS_EMAILS
		fi
		USER_PARAMS_EMAILS[${LOOPS}]="${temp}"
		((LOOPS++))
	done
	LOOPS=1
	ARRCOUNT=${#USER_PARAMS_EMAILS[@]}
	while((1)); do
		read -ep ":[${USER_PARAMS_EMAILS[${LOOPS}]}] "
		if [[ "x${REPLY}" != "x" ]]; then
			if ! echo ${REPLY} | grep '@' > /dev/null ; then
				TEMPVAL="${REPLY}@${USER_PARAMS_PRIM}"
			else
				TEMPVAL="${REPLY}"
			fi
			echo "${USER_PARAMS_EMAILS[@]}" | grep -i -E "^${TEMPVAL}$" > /dev/null 2>&1
			if [[ "$?" == 1 ]]; then
				USER_PARAMS_EMAILS[${LOOPS}]="${TEMPVAL}"
			fi 
		fi
		REPLY="`echo ${REPLY} | ${TOLOWER}`"
		if [[ "x${REPLY}" == "xdelete" ]]; then unset USER_PARAMS_EMAILS[${LOOPS}]; fi


		if [[ ${ARRCOUNT} < ${LOOPS}-1 ]] && [[ "x${REPLY}" == "x" ]]; then
			break;
		fi
		((LOOPS++));
	done
}

function display_user_info(){

	echo "Name:                     ${USER_PARAMS_FIRST} ${USER_PARAMS_LAST}"
	echo "Login:                    ${USER_PARAMS_LOGIN}"
	echo "Description:              ${USER_PARAMS_DESC}"
	echo "Password:                 ${USER_PARAMS_PASSW}"
	if [[ "x${USER_PARAMS_PASS_AUTO}" == "xYES" ]]; then
	echo "Password Explanation:     ${USER_PARAMS_PASSE}"
	fi
	echo "Primary Domain Name:      ${USER_PARAMS_PRIM}"
	for i  in "${USER_PARAMS_EMAILS[@]}"  ; do
	echo "Email Address:            ${i}";
	done
	echo "----------"

}

function flush_user_info(){

	# Flush non persistent user info
	unset USER_PARAMS_EMAILS
	unset USER_PARAMS_LOGIN
	unset USER_PARAMS_PASSW
	unset USER_PARAMS_PASS
	unset USER_PARAMS_PASSE
	unset USER_PARAMS_PASS_AUTO
	if [[ "x${COMPLETE_FLUSH}" == "xYES" ]]; then
		unset USER_PARAMS_FIRST
		unset USER_PARAMS_LAST
		unset USER_PARAMS_DESC
		unset USER_PARAMS_PRIM
	fi
}

function execute_cmd(){

	if [[ "x${DONT_WRITE}" == "xYES" ]]; then
		echo ${1}
	else
		echo ${1} | ${BASH}
	fi



}
function create_user(){
	CMD="useradd ${USER_PARAMS_LOGIN} -c \"${USER_PARAMS_DESC}\" -d ${USERS_MAIL_HOME}${USER_PARAMS_LOGIN} -n -g ${USERS_MAIL_GID} -M -u ${USER_PARAMS_UID} -s /bin/false"

	execute_cmd "${CMD}"
}


function create_user_dir(){
	CMD="mkdir \"${USERS_MAIL_HOME}${USER_PARAMS_LOGIN}\"" 

	execute_cmd "${CMD}"

}

function chown_user_dir(){

	CMD="chown ${USER_PARAMS_LOGIN}:${USERS_MAIL_GID} \"${USERS_MAIL_HOME}${USER_PARAMS_LOGIN}\"" 
	
	execute_cmd "${CMD}"
}

function set_user_passwd(){
	CMD="echo \"${USER_PARAMS_PASSW}\" | ${PASSWD} ${PASSWDARGS} ${USER_PARAMS_LOGIN} > /dev/null 2>&1"

	execute_cmd "${CMD}"
}

function gen_user_passwd(){

	USER_PARAMS_PASS=`${APG} ${APGARGS}`
	USER_PARAMS_PASSW=` echo ${USER_PARAMS_PASS} | cut -f 1 -d' '`
	USER_PARAMS_PASSE=` echo ${USER_PARAMS_PASS} | cut -f 2 -d' '`

}

function generate_uid(){

	USER_PARAMS_UID=${USERS_BASE_UID}
	while [[ $? == 0 ]] && [[ ${USER_PARAMS_UID}<=${USERS_HIGH_UID} ]]; do
		((USER_PARAMS_UID++))
		cat /etc/passwd | cut -f 3 -d':' | grep -E "^$USER_PARAMS_UID$" >/dev/null 
	done
	if [[ ${USERS_PARAMS_UID} > ${USERS_HIGH_UID} ]]; then
		echo "Fatal Error: Generated UID of ${USERS_PARAMS_UID} is higher than ${USERS_HIGH_UID}"
		cleanup_and_exit 2;
	fi
}

function isin_virtdb(){

#	 Check email address in virt table
	local RETVAL

	if [[ "$#" < 1 ]]; then return 1; fi
	${EDITMAP} ${EDITMAP_VIRTDB_ARGS}  $1 > /dev/null 2>&1
	RETVAL="$?"
	if [[ "${RETVAL}" == 0 ]]; then 
		return 1; 
	elif [[ "${RETVAL}" == 69 ]]; then
		return 0;
	else
		echo "Could not check address $1 in virtusertable; error ${RETVAL}"
		read -ep "Continue? (Yes,No):[Yes] "
		REPLY=`echo ${REPLY:0:1} | ${TOLOWER}`
		if [[ "x${REPLY}" != "x" ]] && [[ "x${REPLY}" != "xy" ]]; then
			cleanup_and_exit ${RETVAL}
		
		fi;
		
	fi

}

function isin_sendmail_names(){

#	Check email addresses domain names for sendmail local domain
	local RETVAL
	local tempval

	if [[ "$#" < 1 ]]; then return 1; fi
	tempval=`echo "$1" | cut -f 2 -d '@'`
	cat "${SENDMAIL_LOCAL_NAMES}" | grep -i -E "^${tempval}\$" > /dev/null 2>&1
	RETVAL="$?"
	if [[ "${RETVAL}" == 1 ]]; then
		echo "$1: domain name of ${tempval} not found in ${SENDMAIL_LOCAL_NAMES}"
		return 1;
	fi
}

function putin_virtdb(){
	local temp
	CMD="echo \"${USER_PARAMS_EMAILS[0]} ${USER_PARAMS_LOGIN}\" >> ${VIRTDB_ADDRESSES}"
	execute_cmd "${CMD}"
	
	
	for temp in "${USER_PARAMS_EMAILS[@]}"; do
		if [[ "x${temp}" == "x${USER_PARAMS_EMAILS[0]}" ]]; then continue; fi
		
		CMD="echo \"${temp}		${USER_PARAMS_EMAILS[0]}\" >> ${VIRTDB}"
		execute_cmd "${CMD}"
	done
	
}

function cleanup_and_exit_trap(){

	echo "Signal caught --- cleaning up"
	trap - ${SIGNALS_TO_TRAP} 
	cleanup_and_exit 3
}


function cleanup_and_exit(){

	if [[ "$1" > "0" ]]; then 
		echo "The preceeding user was not created correctly."
		echo "Please investigate and clean manualy"
	fi

	if [[ "x${NEW_USERS}" == "x" ]]; then
		exit
	fi;

	read -ep "Email report? (Yes,No):[Yes] "
	REPLY=`echo ${REPLY:0:1} | ${TOLOWER}`
	if [[ "x${REPLY}" == "x" ]] || [[ "x${REPLY}" == "xy" ]]; then
		mail_new_users_report
	fi
	read -ep "Echo report to screen? (Yes,No):[Yes] "
	REPLY=`echo ${REPLY:0:1} | ${TOLOWER}`
	if [[ "x${REPLY}" == "x" ]] || [[ "x${REPLY}" == "xy" ]]; then
		echo "${NEW_USERS}"
	fi

	read -ep "Write report to file? (Yes,No):[No] "
	REPLY=`echo ${REPLY:0:1} | ${TOLOWER}`
	if [[ "x${REPLY}" == "xy" ]]; then
		write_new_users_report
	fi

	if [[ "x${EXECUTE_MAKEVIRTDB}" != "xYES" ]]; then
		echo "Don't forget to edit the virtusertable and run ${MAKE_VIRTDB}!"
	else
		${MAKE_VIRTDB}
	fi

	exit 0
}

function mail_new_users_report(){
	local i
	local CMDARGS

	REPORT_SUBJECT="New email users created" 
	read -ep "Subject:[${REPORT_SUBJECT}] "
	if [[ "x${REPLY}" != "x" ]]; then REPORT_SUBJECT="${REPLY}"; fi
	
	LOOPS=0
	echo "Enter Destination email addreses. Blank ends/continues."
	while((1)); do
		read -ep ":[${REPORT_EMAILS[${LOOPS}]}] "
		if [[ "x${REPLY}" != "x" ]]; then
			REPORT_EMAILS[${LOOPS}]=${REPLY}
		fi
		if [[ "x${REPORT_EMAILS[${LOOPS}+1]}" == "x" ]] && [[ "x${REPLY}" == "x" ]]; then
			break;
		fi
		((LOOPS++));
	done

	if [[ "x${DEFAULT_REPORT_EMAIL_BCC}" != "xYES" ]]; then
		DEFAULT_REPORT_EMAIL_CCTYPE="-c"
	else
		DEFAULT_REPORT_EMAIL_CCTYPE="-b"
	fi
	
	if [[ "x${REPORT_EMAILS[@]}" == "x" ]]; then
		DEFAULT_REPORT_EMAIL_CCTYPE=""
	fi
	if [[ "x${DEFAULT_REPORT_EMAIL}" == "xYES" ]]; then 
		for i in "${DEFAULT_REPORT_EMAILS[@]}"; do
			CMDARGS="${CMDARGS} ${DEFAULT_REPORT_EMAIL_CCTYPE} $i"
		done
	else
		CMDARGS=""
	fi
	echo "${NEW_USERS}" | mail -s "${REPORT_SUBJECT}" ${REPORT_EMAILS[@]} ${CMDARGS}
}
	
function write_new_users_report(){

	echo Fixme, Not implemented yet


}



function collect_user_info(){

	while((1)); do
		read_user_info
		display_user_info 
		read -ep "All good? (Yes,No):[No] "
		REPLY=`echo ${REPLY:0:1} | ${TOLOWER}`
		if [[ "x${REPLY}" == "xy" ]]; then break; fi

	done
	verify_user_info
 
}
function verify_user_info(){
	local i
	unset CAUGHT	
	if [[ "x${DONT_CHECK_EMAILS}" != "xYES" ]]; then 
		verify_user_info_emails; 
	fi
	if [[ "x${DONT_CHECK_LOGIN}" != "xYES" ]]; then 
		verify_user_info_login; 
	fi
	if [[ "x${CAUGHT}" == "xYES" ]]; then 
		read -ep "Errors were found. Do you wish to correct? (Yes,No):[Yes] "
		REPLY=`echo ${REPLY:0:1} | ${TOLOWER}`
		if [[ "x${REPLY}" != "xn" ]]; then
			collect_user_info; 
		fi
	fi
}

function verify_user_info_emails(){
	for i in ${USER_PARAMS_EMAILS[@]}; do
		if [[ "x${DONT_CHECK_SENDMAIL}" != "xYES" ]]; then
			if ! isin_sendmail_names $i ; then
				CAUGHT="YES"
			fi
		fi
		if ! isin_virtdb $i ; then
			echo "$i is already a valid email address!"
			CAUGHT="YES"			
		fi
	done

}

function verify_user_info_login(){

	cat /etc/passwd | cut -f 1 -d ':' | grep -E "^${USER_PARAMS_LOGIN}$" >/dev/null
	if [[ $? == 0 ]]; then
		echo "Login name ${USER_PARAMS_LOGIN} is already in Use!"
		CAUGHT="YES"
	fi

}

function add_email_user(){

	local RETVAL
	collect_user_info
	generate_uid
	create_user && create_user_dir && chown_user_dir && set_user_passwd && putin_virtdb
	RETVAL=$?
	if [[ ${RETVAL} > 0 ]]; then cleanup_and_exit ${RETVAL} ; fi
	NEW_USERS="${NEW_USERS}
`display_user_info`"
	flush_user_info
}

if [[ "x${TRAP_SIGNALS}" == "xYES" ]]; then
	trap cleanup_and_exit_trap ${SIGNALS_TO_TRAP} 
fi

while((1)); do
	add_email_user
	read -ep "Done? (Yes,No):[No] "
	REPLY=`echo ${REPLY:0:1} | ${TOLOWER}`
	if [[ "x${REPLY}" == "xy" ]]; then break; fi
done;

cleanup_and_exit 0
