#!/bin/bash
# v1.12
# 2025-10-02

# Defaults
MINUID=1000
GROUP_ID=1001
SHADOW_MAX=99999
HOME_BASE='/var/mail'
IS_ACTIVE='TRUE'

. /etc/ntools/mail-config

function usage() {
cat << EOF
usage $0 [options] [username | mail@address]

This script add a mail user in LDAP

OPTIONS
  -h         Show this message
  -a <alias> Mail alias in <user@domain> format: -a "alias1[ alias2[ ...]]"
  -r <addr>  Mail Recipient BCC in <user@domain> format
  -s <addr>  Mail Sender BCC in <user@domain> format
  -u <name>  System username (forexample: <user_domain>)
  -n <name>  Username
  -p <pass>  Password
  -d <dom>   Domain
  -i <uid>   User ID Number (default: next available number)
  -g <gid>   Primary group ID Number
  -D <dn>    LDAP Manager DN
  -w <pass>  LDAP Manager password
  -U <dn>    LDAP Users DN
  -G <dn>    LDAP Groups DN
  -M <dn>    LDAP Domains DN
  -b         Run in non-interactive mode
  -v         Verbose
  -x         Disable user
EOF
	exit
}

function check_mail_address {
	local MA
	MA="$1"

	local ADDRESS
	ADDRESS=$(ldapsearch -x -w "$LDAP_BIND_PASSWORD" -b "${LDAP_USERS_DN}" -D "${LDAP_BIND_DN}" -LLL "(&(objectClass=mailAccount)(mailAddress=$MA))" mailAddress | grep -e '^mail')
	[ -n "$ADDRESS" ] && { echo "Error! EMail $MA already exists."; exit 1; }

	# Check aliases
	ADDRESS=$(ldapsearch -x -w "$LDAP_BIND_PASSWORD" -b "${LDAP_USERS_DN}" -D "${LDAP_BIND_DN}" -LLL "(&(objectClass=mailAccount)(mailAlias=$MA))" mailAddress | grep -e '^mail' | cut -d':' -f2 | sort | xargs)
	[ -n "$ADDRESS" ] && { echo "Error! EMail $MA already used as alias for $ADDRESS."; exit 1; }
}

function get_password {
	local n
	n=8
	local PWGEN
	PWGEN=$(pwgen "$n" 2 | xargs | tr ' ' '-')
	echo "${PWGEN}"
}

while getopts ":ha:r:s:u:n:p:d:i:g:D:w:U:G:M:bvx" OPTION; do
	case $OPTION in
		h)
			usage
		;;
		a)
			MAIL_ALIAS="$OPTARG"
		;;
		r)
			MAIL_RECIPIENT_BCC+="$OPTARG "
		;;
		s)
			MAIL_SENDER_BCC+="$OPTARG "
		;;
		u)
			SYS_USER_NAME=$OPTARG
		;;
		n)
			USER_NAME=$OPTARG
		;;
		p)
			USER_CLEARTEXT_PASS=$OPTARG
		;;
		d)
			MAIL_DOMAIN=$OPTARG
		;;
		i)
			USER_ID=$OPTARG
		;;
		g)
			GROUP_ID=$OPTARG
		;;
		D)
			LDAP_BIND_DN=$OPTARG
		;;
		w)
			LDAP_BIND_PASSWORD=$OPTARG
		;;
		U)
			LDAP_USERS_DN=$OPTARG
		;;
		G)
			LDAP_GROUPS_DN=$OPTARG
		;;
		M)
			LDAP_DOMAINS_DN=$OPTARG
		;;
		b)
			IS_NIMODE=1
		;;
		v)
			IS_VERBOSE=1
		;;
		x)
			IS_ACTIVE='FALSE'
		;;
		\?)
			echo "Invalid option: -$OPTARG" >&2
			usage
		;;
		:)
			echo "Option -$OPTARG requires an argument." >&2
			usage
		;;
	esac
done

shift $((OPTIND -1))

MAIL_ADDRESS="$1"

[ -z "$2" ] || usage

while [ -z "$LDAP_BIND_DN" ]; do
	[ "$IS_NIMODE" ] && { echo "Error! LDAP Bind DN missed.";exit 1; }
	read -rp "LDAP Manager dn: " LDAP_BIND_DN
done

if [ -z "$LDAP_BIND_PASSWORD" ]; then
	read -rp "LDAP Manager Password: " -s LDAP_BIND_PASSWORD
	echo
fi

while [ -z "$LDAP_USERS_DN" ]; do
	[ "$IS_NIMODE" ] && { echo "Error! LDAP Users DN missed.";exit 1; }
	read -rp "LDAP Users dn: " LDAP_USERS_DN
done

while [ -z "$LDAP_GROUPS_DN" ]; do
	[ "$IS_NIMODE" ] && { echo "Error! LDAP Users DN missed.";exit 1; }
	read -rp "LDAP Groups dn: " LDAP_GROUPS_DN
done

while [ -z "$LDAP_DOMAINS_DN" ]; do
	[ "$IS_NIMODE" ] && { echo "Error! LDAP Domains DN missed.";exit 1; }
	read -rp "LDAP Domains dn: " LDAP_DOMAINS_DN
done

if [ -n "$MAIL_ADDRESS" ]; then
	USER_NAME=$(echo "$MAIL_ADDRESS" | cut -d@ -f1)
	DOMAIN_NAME=$(echo "$MAIL_ADDRESS" | cut -s -d@ -f2)
	[ -z "$MAIL_DOMAIN" ] && MAIL_DOMAIN=$(echo "$MAIL_ADDRESS" | cut -s -d@ -f2)
	if [ -z "$DOMAIN_NAME" ]; then
		MAIL_ADDRESS=''
	else
		if [ "$MAIL_DOMAIN" != "$DOMAIN_NAME" ]; then
			echo "Domain in address $MAIL_ADDRESS conflicts with domain $MAIL_DOMAIN."
			exit 1;
		fi
	fi
fi

DOMAINS=$(ldapsearch -x -w "$LDAP_BIND_PASSWORD" -b "${LDAP_DOMAINS_DN}" -D "${LDAP_BIND_DN}" -LLL "(objectClass=mailDomain)" mailDomain | grep -e '^mail' | cut -d':' -f2 | sort)

if [ -z "$DOMAINS" ]; then
	echo "Mail domains not found. Please run 'maildomain add ...' first."
	exit 1
fi

if [ -n "$MAIL_DOMAIN" ]; then
	for cdom in $DOMAINS; do
		[ "$cdom" == "$MAIL_DOMAIN" ] && { DOMAINEXISTS=1; break; }
	done
	[ "$DOMAINEXISTS" ] || { echo "Error! Domain $MAIL_DOMAIN not exists.";exit 1; }
else
	[ "$IS_NIMODE" ] && { echo "Error! Mail Domain missed.";exit 1; }
	while [ -z "$MAIL_DOMAIN" ]; do
		echo "Select mail domain from list:"
		DOMARR=()
		for cdom in $DOMAINS; do
			ACTIVE=$(ldapsearch -x -w "$LDAP_BIND_PASSWORD" -b "${LDAP_DOMAINS_DN}" -D "${LDAP_BIND_DN}" -LLL "(&(objectClass=mailDomain)(mailDomain=${cdom}))" active | grep -e '^active' | cut -d':' -f2)
			STATE='unknown'
			[ "$ACTIVE" == " TRUE" ] && STATE='active'
			[ "$ACTIVE" == " FALSE" ] && STATE='inactive'
			echo "${#DOMARR[*]}: $cdom [$STATE]"
			DOMARR+=("$cdom")
		done
		read -rp "Enter number of domain [0]: " MAIL_DOMAIN_NUMBER
		[ -z "$MAIL_DOMAIN_NUMBER" ] && MAIL_DOMAIN_NUMBER=0
		[[ "$MAIL_DOMAIN_NUMBER" =~ ^[0-9]+$ ]] && MAIL_DOMAIN="${DOMARR[$MAIL_DOMAIN_NUMBER]}"
	done
fi

while [ -z "$USER_NAME" ]; do
	[ "$IS_NIMODE" ] && { echo "Error! User name missed.";exit 1; }
	read -rp "User name for @${MAIL_DOMAIN}: " USER_NAME
done
USER_NAME="${USER_NAME,,}"

[ -z "$MAIL_ADDRESS" ] && MAIL_ADDRESS="${USER_NAME}@${MAIL_DOMAIN}"
check_mail_address "$MAIL_ADDRESS"

[ -z "$SYS_USER_NAME" ] && SYS_USER_NAME="${USER_NAME}_${MAIL_DOMAIN}"

if [ -z "$USER_CLEARTEXT_PASS" ]; then
	USER_CLEARTEXT_PASS=$(get_password)
	if [ "$IS_NIMODE" ]; then
		echo "Password for ${MAIL_ADDRESS} [$USER_CLEARTEXT_PASS]"
	else
		read -rp "Password for ${MAIL_ADDRESS} [$USER_CLEARTEXT_PASS]: " -s CUR_PASS
		echo
		[ -n "$CUR_PASS" ] && USER_CLEARTEXT_PASS="$CUR_PASS"
	fi
fi

USER_PASS=$(slappasswd -h "{SSHA}" -s "$USER_CLEARTEXT_PASS")

unset USER_CLEARTEXT_PASS

CHANGE_DATE=$(($(date +%s)/86400))

# Mail Aliases
if ! [ "$IS_NIMODE" ]; then
	READTEXT="x"
	while [ -n "$READTEXT" ]; do
		read -rp "Aliases [${MAIL_ALIAS}]: " READTEXT
		READTEXT="${READTEXT,,}"
		[ -z "$READTEXT" ] && continue
			if [[ "$READTEXT" =~ ^\- ]]; then
			READTEXT="$(echo "${READTEXT}" | cut -f1 -d' ')"
			MAIL_ALIAS="$({ for a in ${MAIL_ALIAS}; do if [ "$a" != "${READTEXT:1}" ]; then echo "$a"; fi; done; } | xargs)"
		elif [[ "$READTEXT" =~ ^\+ ]]; then
			READTEXT="$(echo "${READTEXT}" | cut -f1 -d' ')"
			MAIL_ALIAS="$({ for a in ${MAIL_ALIAS}; do echo "$a"; done; echo "${READTEXT:1}"; } | sort -u | xargs)"
		else
			MAIL_ALIAS="$({ for a in ${MAIL_ALIAS}; do echo "$a"; done; for a in ${READTEXT}; do echo "$a"; done; } | sort -u | xargs)"
		fi
	done
fi

if [ -z "${SN}" ]; then
	SN="${USER_NAME}"
	if ! [ "$IS_NIMODE" ]; then
		read -rp "Фамилия [${SN}]: " READTEXT
		if [ -n "${READTEXT}" ]; then
			SN="${READTEXT}"
		fi
	fi
fi

if [ -z "${GN}" ]; then
	if ! [ "$IS_NIMODE" ]; then
		read -rp "Имя: " GN
	fi
fi

if [ -z "${INI}" ]; then
	if ! [ "$IS_NIMODE" ]; then
		read -rp "Отчество: " INI
	fi
fi

DISPN="${SN} ${GN} ${INI}"
#Remove squeeze repeat spaces
DISPN=$(echo "${DISPN}" | xargs)
if ! [ "$IS_NIMODE" ]; then
	read -rp "Отображаемое имя [${DISPN}]: " READTEXT
	if [ -n "$READTEXT" ]; then
		DISPN="$READTEXT"
	fi
fi
if [ -z "$MOBILE" ]; then
	if ! [ "$IS_NIMODE" ]; then
		read -rp "mobile: " MOBILE
	fi
fi

if [ -z "$DESCRIPTION" ]; then
	if ! [ "$IS_NIMODE" ]; then
		read -rp "Description: " DESCRIPTION
	fi
fi

# If USER_ID not supplied, find next using ldap query
if [ -z "$USER_ID" ]; then
	HIGHEST_UID=$(ldapsearch -x -w "$LDAP_BIND_PASSWORD" -b "${LDAP_USERS_DN}" -D "${LDAP_BIND_DN}" -LLL "(objectClass=posixAccount)" uidNumber | grep -e '^uid' | cut -d':' -f2 | sort | tail -1)
	if [ -z "$HIGHEST_UID" ]; then
		HIGHEST_UID="$MINUID"
	fi
	USER_ID=$((HIGHEST_UID+1))
fi

LDIF=$(cat<<EOF
dn: uid=${SYS_USER_NAME},${LDAP_USERS_DN}
changetype: add
uid: ${SYS_USER_NAME}
cn: ${USER_NAME}
sn: ${SN}
objectClass: top
objectClass: posixAccount
objectClass: shadowAccount
objectClass: inetOrgPerson
objectClass: mailAccount
userPassword: ${USER_PASS}
shadowLastChange: ${CHANGE_DATE}
shadowMax: ${SHADOW_MAX}
shadowWarning: 7
loginShell: /bin/false
uidNumber: ${USER_ID}
gidNumber: ${GROUP_ID}
homeDirectory: ${HOME_BASE}/${MAIL_DOMAIN}/${USER_NAME}
mailAddress: ${MAIL_ADDRESS}
mail: ${MAIL_ADDRESS}
mailDomain: ${MAIL_DOMAIN}
mailMailbox: mail
active: ${IS_ACTIVE}
EOF
)

if [ -n "$GN" ]; then
	LDIF="$LDIF
gn: ${GN}"
fi

if [ -n "$INI" ]; then
	LDIF="$LDIF
initials: ${INI}"
fi

if [ -n "$DISPN" ]; then
	LDIF="$LDIF
displayName: ${DISPN}"
fi

if [ -n "$MOBILE" ]; then
	LDIF="$LDIF
mobile: ${MOBILE}"
fi

if [ -n "$DESCRIPTION" ]; then
	LDIF="$LDIF
description: $DESCRIPTION"
fi

for item in ${MAIL_ALIAS}; do
	LDIF="$LDIF
mailAlias: ${item}"
done

for item in $MAIL_RECIPIENT_BCC; do
	LDIF="$LDIF
mailRecipientBCC: ${item}"
done

for item in $MAIL_SENDER_BCC; do
	LDIF="$LDIF
mailSenderBCC: ${item}"
done

if [ "$IS_VERBOSE" ]; then
	echo "--------------------"
	echo "Adding ${LDIF}"
	echo "--------------------"
fi

#if getent passwd | cut -d: -f3 | grep -e "^${USER_ID}$" >/dev/null; then
#	echo "System user with id=${USER_ID} already exists!"
#	exit 1
#fi

RESULT=$(echo "$LDIF" | ldapmodify -x -w "${LDAP_BIND_PASSWORD}" -D "${LDAP_BIND_DN}")
[ "$IS_VERBOSE" ] && echo "$RESULT"
unset LDAP_BIND_PASSWORD

#mkdir -p -m 700 "${HOME_BASE}/${MAIL_DOMAIN}/${USER_NAME}" || { echo "Error! Can't create home dir ${HOME_BASE}/${MAIL_DOMAIN}/${USER_NAME}";exit 1; }
#chown "${USER_ID}":"${GROUP_ID}" "${HOME_BASE}/${MAIL_DOMAIN}/${USER_NAME}"

