#!/bin/sh
# update-exim4.conf(8) - Generate /var/lib/exim4/config.autogenerated


UPEX4C_confdir="/etc/exim4"
UPEX4C_sections="main acl router transport retry rewrite auth"
EXIM="/usr/sbin/exim4"

UPEX4C_verbose=no
UPEX4C_autoconfigfile=/var/lib/exim4/config.autogenerated
UPEX4C_outputfile="${UPEX4C_autoconfigfile}"
UPEX4C_version="4.50-8sarge2"

UE4CC="$UPEX4C_confdir/update-exim4.conf.conf"

usage() {
cat <<EOF
$0 - Generate exim4 configuration files
       Options:
       -v|--verbose - Enable verbose mode, tell about ignored files
       -h|--help - Show this message
       --keepcomments - Do not remove comment lines
       --removecomments - Remove comment lines
       -o|--output file - write output to file instead of ${UPEX4C_outputfile}
       -d|--confdir directory - read input from given directory instead of ${UPEX4C_confdir}
EOF
}

## Parse commandline
TEMP=$(getopt -n update-exim4.conf \
	-l keepcomments,removecomments,output:,confdir:,help,verbose -- \
	+o:d:vh "$@")

if test "$?" != 0; then
	echo "Terminating..." >&2
	exit 1
fi

eval set -- ${TEMP}
while test "$1" != "--"; do
	case $1 in
		-h|--help)
			usage
			exit 0
		;;
		-v|--verbose)
			UPEX4C_verbose=yes
		;;
		--keepcomments)
			UPEX4C_comments=yes
		;;
		--removecomments)
			UPEX4C_comments=no
		;;
		-o|--output)
			shift
			UPEX4C_outputfile="$1"
		;;
		-d|--confdir)
			shift
			UPEX4C_confdir="$1"
		;;
	esac
	shift
done
shift

# No non-option arguments allowed.
if [ "$#" -ne 0 ]; then
	echo "No non option arguments ($@) allowed" >&2
	usage >&2
	exit 1
fi

# exit immediately if /etc/exim4/exim4.conf exists and -o was not specified
if [ -e /etc/exim4/exim4.conf ] && \
  [ "x${UPEX4C_outputfile}" = "x${UPEX4C_autoconfigfile}" ] ; then
  exit 0
fi


UPEX4C_confd=$UPEX4C_confdir/conf.d

[ -d ${UPEX4C_confd} ] || \
{ printf "$0: Error, no ${UPEX4C_confd}, exiting.\n" 1>&2 ; exit 1 ; }

[ -d `dirname $UPEX4C_outputfile` ] || \
{ printf "$0: Error, missing `dirname $UPEX4C_outputfile`, exiting.\n" 1>&2 ; exit 1 ; }

if [ -f "$UE4CC" ]; then
  . $UE4CC
else
  echo >&2 "$0: Error, no $UE4CC, exiting."
  exit 1
fi

[ "x${CFILEMODE}" = "x" ] && CFILEMODE=644
[ "x${dc_use_split_config}" = "x" ] && dc_use_split_config='false'
[ "x${dc_localdelivery}" = "x" ] && dc_localdelivery='mail_spool'
[ "x${UPEX4C_comments}" = "x" ] && UPEX4C_comments="${ue4c_keepcomments:-no}"

mailname=`cat /etc/mailname | head -n 1`

# add localhost, get rid of spaces and trailing colons
local_domains="`echo localhost:${dc_other_hostnames} | \
	sed -e 's�[: ]*$��' -e 's� *��'`"

dc_rewriteemailaddresses_mailname='*@'"$mailname"' ${lookup{${local_part}}lsearch{/etc/email-addresses}{$value}fail} Ffrs'

TEMPLATEFILE=${UPEX4C_confdir}/exim4.conf.template

UPEX4C_internal_tmp=`tempfile -m600 -p ex4`

trap "rm -f ${UPEX4C_internal_tmp}" EXIT INT TERM

# test if $1 is user modified, print message
unmodifandmessage() {
	[ "$#" -eq 1 ] || return 1
	if unmodified "$1" ; then
		return 0
	else
		[ "${UPEX4C_verbose}" = "yes" ] && \
			echo "ignoring user modified file $1"
		return 1
	fi
}	

# 0123456789abcdef0123456789abcdef



# use this as template for new gen_something functions.
UPEX4C_skeleton() {
	UPEX4C_internal_currfile="${UPEX4C_confd}/foo/bar"
	unmodifandmessage "${UPEX4C_internal_currfile}" || return
	cat << EOF > "${UPEX4C_internal_tmp}"
# ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
# This is an md5sum.
#######################################
# WARNING WARNING WARNING WARNING
# This file is dynamically generated by update-exim4.conf(8) using the data
# in /etc/exim4/update-exim4.conf.conf.
EOF
# insert more code that appends to ${UPEX4C_internal_tmp}
	UPEX4C_internal_md5=`cat "${UPEX4C_internal_tmp}" | md5sum | cut -d\  -f1`
	{ echo "# ${UPEX4C_internal_md5}" ; cat "${UPEX4C_internal_tmp}" ; } > \
		"${UPEX4C_internal_currfile}"
}

# run-parts emulation, stolen from Branden's /etc/X11/Xsession
# Addition: Use file.rul instead if file if it exists.
run_parts () {
	# reset LC_COLLATE
	unset LANG LC_COLLATE LC_ALL

	if [ -z "$1" ]; then
		errormessage "$0: internal run_parts called without an argument"
	fi
	if [ ! -d "$1" ]; then
		errormessage "$0: internal run_parts called, but $1 does not exist or is not a directory."
	fi
	for F in $(ls $1); do
		if expr "$F" : '[[:alnum:]_-]\+$' > /dev/null 2>&1; then
			if [ -f "$1/$F" ] ; then
				if [ -f "$1/${F}.rul" ] ; then
					echo "$1/${F}.rul"
				else
					echo "$1/$F"
				fi
			fi
		fi
	done;
}
# also from Branden
errormessage () {
	# pretty-print messages of arbitrary length (no trailing newline)
	echo "$*" | fold -s -w ${COLUMNS:-80} >&2;
}

cat_parts() {
       if [ -z "$1" ]; then
               errormessage "$0: internal cat_parts called without an argument"
       fi
       if [ ! -d "$1" ]; then
               errormessage "$0: internal cat_parts called, but $1 does not exist or is not a directory."
       fi
       for file in `run_parts $1`; do
               echo "#####################################################"
               echo "### $file"
               echo "#####################################################"
               cat $file
	       echo
               echo "#####################################################"
               echo "### end $file"
               echo "#####################################################"
       done
}

# check whether the file given as argument was modified by the user
# by comparing the md5sum in the first line with the real one.
unmodified() {
	[ "$#" -eq 1 ] || return 1
	[ -f "$1" ] || return 1
	# first line, without the leading '# '.
	checksum_current=`sed -n -e '1s/^# //' -e '1p;1q' "$1"`

	# md5sum over the rest of the file.
	# some versions of md5sum produce
	# '68b329da9893e34099c7d8ad5cb9c940  -' others don't add the dash.
	# '68b329da9893e34099c7d8ad5cb9c940'
	checksum_new=`sed -n '2,$p' "$1" | md5sum | cut -d\  -f1`

	if [ "${checksum_current}" = "${checksum_new}" ] ; then
		return 0
	else
		return 1
	fi
}

# update the md5sum given in the first line, return an error if the file does
# not have an md5sum header in the first line.
updatechecksumheader() {
	[ "$#" -eq 1 ] || return 1
	[ -f "$1" ] || return 1
	# check for correct format "# 76a51391da4a0687697224a124b71a17"
	sed -n -e '1p;1q' "$1" | grep -q -E '^# [[:xdigit:]]{32}$' || \
		{ echo "incorrect format" ; return 1 ;}

	NEWFILE=`tempfile -m600 -p ex4`
	sed -n '2,$p' "$1" | md5sum | cut -d\  -f1 | sed '1s/^/# /' > $NEWFILE
	sed -n '2,$p' "$1" >> $NEWFILE
	mv -f $NEWFILE "$1"
}

gentmpconf() {
	touch ${UPEX4C_outputfile}.tmp
	#chown --reference=${TEMPLATEFILE} \
	#	${UPEX4C_outputfile}.tmp ${UPEX4C_outputfile}
	#chmod --reference=${TEMPLATEFILE} \
	#		${UPEX4C_outputfile}.tmp ${UPEX4C_outputfile}
	if [ "`id -u`" = "0" ]; then 
		chown root:Debian-exim ${UPEX4C_outputfile}.tmp
		[ -e ${UPEX4C_outputfile} ] && \
		chown root:Debian-exim ${UPEX4C_outputfile}
	fi
	chmod 640 ${UPEX4C_outputfile}.tmp
	[ -e ${UPEX4C_outputfile} ] && chmod 640 ${UPEX4C_outputfile}
}

removecomments(){
	if [ "x${UPEX4C_comments}" = "xno" ] ; then
		grep -E -v '^[[:space:]]*#' | sed -e '/^$/N;/\n$/D' ;
	else
		cat
	fi
}

gentmpconf

cat << EOF > ${UPEX4C_outputfile}.tmp
#########
# WARNING WARNING WARNING
# WARNING WARNING WARNING
# WARNING WARNING WARNING
# WARNING WARNING WARNING
# WARNING WARNING WARNING
# this file is generated dynamically from the files in
# CONFDIR/conf.d/ or /etc/exim4/exim4.conf.template respectively and
# /etc/exim4/update-exim4.conf.conf
# Any changes you make here will be lost.
# See /usr/share/doc/exim4-base/README.Debian.gz and update-exim4.conf(8)
# for instructions of customization.
# WARNING WARNING WARNING
# WARNING WARNING WARNING
# WARNING WARNING WARNING
# WARNING WARNING WARNING
# WARNING WARNING WARNING
#########
EOF

case "$dc_eximconfig_configtype" in
	satellite|smarthost)
	if [ "${dc_hide_mailname}" = "true" ] && [ -n "${dc_readhost}" ] ; then
		DEBCONFheaders_rewriteDEBCONF='headers_rewrite = *@+local_domains $1@DCreadhost frs : *@'"$mailname"' $1@DCreadhost frs'
		DEBCONFreturn_pathDEBCONF='return_path = ${if match_domain{$sender_address_domain}{+local_domains}{${sender_address_local_part}@DCreadhost}{${if match_domain{$sender_address_domain}{'"$mailname"'}{${sender_address_local_part}@DCreadhost}fail}}}'
	fi
	;;
	local)
	;;
	internet)
	;;
	none|*)
                if [ "${dc_use_split_config}" = "true" ] ; then
                        for i in ${UPEX4C_sections} ; do
                                cat_parts ${UPEX4C_confd}/$i
                        done | \
                        removecomments | \
                        sed -e "s/DEBCONF[^D][^E][^B].*DEBCONF//g" \
			>> ${UPEX4C_outputfile}.tmp
                else
			LOCALMACROS=""
			if [ -e "/etc/exim4/exim4.conf.localmacros" ]; then
			  LOCALMACROS="/etc/exim4/exim4.conf.localmacros"
			fi
                        cat $LOCALMACROS /etc/exim4/exim4.conf.template | \
                        removecomments | \
                        sed -e "s/DEBCONF[^D][^E][^B].*DEBCONF//g" \
                                >> ${UPEX4C_outputfile}.tmp
                fi
		mv -f ${UPEX4C_outputfile}.tmp ${UPEX4C_outputfile}
		chmod ${CFILEMODE} ${UPEX4C_outputfile}
		[ "${UPEX4C_verbose}" = "yes" ] && \
			echo "Not substituting variables since conftype is none (or other)"
		exit 0
	;;
esac

if [ "x${dc_local_interfaces}" = "x" ] ; then
	listenonpublic='# if local_interfaces is unset, we listen on all interfaces'
else
	listenonpublic="local_interfaces = ${dc_local_interfaces}"
fi

hardcode_primary_hostname=""
if [ "x${dc_minimaldns}" = "xtrue" ] ; then
	UPEX4C_minimaldns='DC_minimaldns = 1'
	if guessed_name=`hostname --fqdn | grep '\.'` ; then
		hardcode_primary_hostname="primary_hostname = ${guessed_name}"
	fi
else
	UPEX4C_minimaldns=''
fi

case "${dc_use_split_config}" in
true)
	for i in ${UPEX4C_sections} ; do
		echo "# begin processing $i #####"
		cat_parts ${UPEX4C_confd}/$i
		echo "# end of $i  #####"
	done | \
	sed -e "s�DEBCONFlocal_domainsDEBCONF�@:${local_domains}�g" \
		-e "s�DEBCONFrelay_domainsDEBCONF�${dc_relay_domains}�g" \
		-e "s�DEBCONFrelay_netsDEBCONF�${dc_relay_nets}�g" \
		-e "s�DEBCONFvisiblenameDEBCONF�${mailname}�g" \
		-e "s�DEBCONFreadhostDEBCONF�${dc_readhost}�g" \
		-e "s�DEBCONFsmarthostDEBCONF�${dc_smarthost}�g" \
		-e "s�DEBCONFconfigtypeDEBCONF�${dc_eximconfig_configtype}�g" \
		-e "s�DEBCONFlistenonpublicDEBCONF�${listenonpublic}�g" \
		-e "s�DEBCONFpackageversionDEBCONF�${UPEX4C_version}�g" \
		-e "s�DEBCONFminimaldnsDEBCONF�${UPEX4C_minimaldns}�g" \
		-e "s�DEBCONFnever_usersDEBCONF��g" \
		-e "s�DEBCONFheaders_rewriteDEBCONF�${DEBCONFheaders_rewriteDEBCONF}�g" \
		-e "s�DEBCONFreturn_pathDEBCONF�${DEBCONFreturn_pathDEBCONF}�g" \
		-e "s�DEBCONF_hardcode_primary_hostname_DEBCONF�${hardcode_primary_hostname}�g" \
		-e "s�DEBCONFlocaldeliveryDEBCONF�${dc_localdelivery}�g" \
		-e "s�DEBCONFrewriteemailaddresses_mailnameDEBCONF�${dc_rewriteemailaddresses_mailname}�g" \
	| removecomments \
		>> ${UPEX4C_outputfile}.tmp
;;
false)
	if [ ! -r /etc/exim4/exim4.conf.template ] ; then
		echo "Error: Unsplit config selected and /etc/exim4/exim4.conf.template missing ... exiting" 1>&2
		exit 1
	fi
	LOCALMACROS=""
	if [ -e "/etc/exim4/exim4.conf.localmacros" ]; then
		LOCALMACROS="/etc/exim4/exim4.conf.localmacros"
	fi
	cat $LOCALMACROS /etc/exim4/exim4.conf.template \
	| sed -e "s�DEBCONFlocal_domainsDEBCONF�@:${local_domains}�g" \
		-e "s�DEBCONFrelay_domainsDEBCONF�${dc_relay_domains}�g" \
		-e "s�DEBCONFrelay_netsDEBCONF�${dc_relay_nets}�g" \
		-e "s�DEBCONFvisiblenameDEBCONF�${mailname}�g" \
		-e "s�DEBCONFreadhostDEBCONF�${dc_readhost}�g" \
		-e "s�DEBCONFsmarthostDEBCONF�${dc_smarthost}�g" \
		-e "s�DEBCONFconfigtypeDEBCONF�${dc_eximconfig_configtype}�g" \
		-e "s�DEBCONFlistenonpublicDEBCONF�${listenonpublic}�g" \
		-e "s�DEBCONFpackageversionDEBCONF�${UPEX4C_version}�g" \
		-e "s�DEBCONFminimaldnsDEBCONF�${UPEX4C_minimaldns}�g" \
		-e "s�DEBCONFnever_usersDEBCONF��g" \
		-e "s�DEBCONFheaders_rewriteDEBCONF�${DEBCONFheaders_rewriteDEBCONF}�g" \
		-e "s�DEBCONFreturn_pathDEBCONF�${DEBCONFreturn_pathDEBCONF}�g" \
		-e "s�DEBCONF_hardcode_primary_hostname_DEBCONF�${hardcode_primary_hostname}�g" \
		-e "s�DEBCONFlocaldeliveryDEBCONF�${dc_localdelivery}�g" \
		-e "s�DEBCONFrewriteemailaddresses_mailnameDEBCONF�${dc_rewriteemailaddresses_mailname}�g" \
	| removecomments \
		>> ${UPEX4C_outputfile}.tmp
;;
esac

# test validity if called without -o
if [ "x${UPEX4C_outputfile}" = "x${UPEX4C_autoconfigfile}" ] && \
	[ -x ${EXIM} ] ; then
	if ! ${EXIM} -C "${UPEX4C_outputfile}.tmp" -bV > /dev/null ; then
	        # we have an error in the configuration file. Do not install
		# and activate. However, errors in string expansions inside
		# the configuration file are not detected by this check!
		errormessage "Invalid new configfile ${UPEX4C_outputfile}.tmp"
		errormessage "not installing ${UPEX4C_outputfile}.tmp to ${UPEX4C_outputfile}"
		exit 1
	fi
fi

mv -f ${UPEX4C_outputfile}.tmp ${UPEX4C_outputfile}
chmod ${CFILEMODE} ${UPEX4C_outputfile}
