#!/bin/bash
#
# Parse Sendmail config for databases
#
# Copyright (c) 1999, Richard Nelson <cowboy@debian.org>.
# Time-stamp: <1999/05/19 10:00:00 cowboy>
#
# Notes (to all):
#   * assumes makemap dbtype /etc/mail/database < /etc/mail/database
#   * Does *not* run with sh=ash (no echo -e support)
#
# Notes (to self):
#   * changes made herein *must* be reflected in parsemc,updatedb,debian.m4
#   * remember to change gawk to awk before release (gawk is more anal)
#   * multiple db options not supported
#   * chmod/chown of secondary alias databases not being done - no harm...
#   * userdb can also have multiple databases and then a forward!
#   * need sendmail stop/start
#
# List of db features
db_features="access_db bitdomain domaintable genericstable \
	     mailertable uucpdomain virtusertable use_cw_file use_ct_file";
export db_features;
# kluge for userdb, cr, ct, and cw files support
db_files="confCR_FILE confUSERDB_SPEC ALIAS_FILE";
db_files="sendmail.mc $db_features $db_files";
export db_files;
#
# Variables for documentation
date=$(date)
export date
user=$USER
export user
hostname=$(hostname)
export hostname
script=$0
export script
directory=$(pwd)
export directory

#------------------------------------------------------------------------
# My first awk program ;-}
pattern='BEGIN { \
		# List of db features, split into an array \
		str = ENVIRON["db_features"]; \
		split(str, db_features); \
		str = ENVIRON["db_files"]; \
		split(str, db_files); \
		# Prefill entries based upon sendmail/debian defaults \
		str = "sendmail.mc m4 - /etc/mail/sendmail.mc -"; \
		split(str, entry); \
		dbs[entry[1]] = entry[2]":"entry[3]":"entry[4]":"entry[5]":"; \
		str = "MAIL_SETTINGS_DIR /etc/mail/ - - -"; \
		split(str, entry); \
		db_loc=entry[2]; \
		dbs[entry[1]] = entry[2]":"entry[3]":"entry[4]":"entry[5]":"; \
		str = "DATABASE_MAP_TYPE hash - - -"; \
		split(str, entry); \
		db_type=entry[2]; \
		dbs[entry[1]] = entry[2]":"entry[3]":"entry[4]":"entry[5]":"; \
		str = "confUSERDB_SPEC btree -o /etc/mail/users -"; \
		split(str, entry); \
		dbs[entry[1]] = entry[2]":"entry[3]":"entry[4]":"entry[5]":"; \
		str = "confCR_FILE - -o /etc/mail/relay-domains %[^\#]"; \
		split(str, entry); \
		dbs[entry[1]] = entry[2]":"entry[3]":"entry[4]":"entry[5]":"; \
		str = "confCT_FILE - -o /etc/mail/trusted-users %[^\#]"; \
		split(str, entry); \
		dbs[entry[1]] = entry[2]":"entry[3]":"entry[4]":"entry[5]":"; \
		str = "confCW_FILE - -o /etc/mail/local-host-names %[^\#]"; \
		split(str, entry); \
		dbs[entry[1]] = entry[2]":"entry[3]":"entry[4]":"entry[5]":"; \
		str = "ALIAS_FILE newaliases - /etc/mail/aliases -"; \
		split(str, entry); \
		dbs[entry[1]] = entry[2]":"entry[3]":"entry[4]":"entry[5]":"; \
		}; \
	# Look for default database location \
	# define(MAIL_SETTINGS_DIR, /etc/mail/)dnl # comment \
	/^[:space:]*define\([:space:]*`?MAIL_SETTINGS_DIR/ { \
		str = $0; \
		# Strip garbage from string \
		sub(/define\(/, "", str); \
		sub(/\)(dnl)?.*/, "", str); \
		sub(/\,/, "", str); \
		gsub(/`/, "", str); \
		gsub(/'"'"'/, "", str); \
		split(str, entry); \
		entry[5] = "-"; \
		entry[4] = "-"; \
		entry[3] = "-"; \
		db_loc=entry[2]; \
		# Save lastmost entry \
		dbs[entry[1]] = entry[2]":"entry[3]":"entry[4]":"entry[5]":"; \
                } \
	# Look for default database type \
	# define(DATABASE_MAP_TYPE, hash)dnl # comment \
	/^[:space:]*define\([:space:]*`?DATABASE_MAP_TYPE/ { \
		str = $0; \
		# Strip garbage from string \
		sub(/define\(/, "", str); \
		sub(/\)(dnl)?.*/, "", str); \
		sub(/\,/, "", str); \
		gsub(/`/, "", str); \
		gsub(/'"'"'/, "", str); \
		split(str, entry); \
		entry[5] = "-"; \
		entry[4] = "-"; \
		entry[3] = "-"; \
		db_type=entry[2]; \
                # Save lastmost entry \
		dbs[entry[1]] = entry[2]":"entry[3]":"entry[4]":"entry[5]":"; \
		} \
	# Look for userdb specification \
	# define(confUSERDB_SPEC, /etc/mail/users.db)dnl # comment \
	/^[:space:]*define\([:space:]*`?confUSERDB_SPEC/ { \
		str = $0; \
		# Strip garbage from string \
		sub(/define\(/, "", str); \
		sub(/\)(dnl)?.*/, "", str); \
		sub(/\,/, "", str); \
		gsub(/`/, "", str); \
		gsub(/'"'"'/, "", str); \
		split(str, entry); \
		entry[5] = "-"; \
		entry[4] = entry[2]; \
		entry[3] = "-o"; \
		entry[2] = "btree"; \
		# Save lastmost entry \
		sub(/\.db/, "", entry[4]); \
		dbs[entry[1]] = entry[2]":"entry[3]":"entry[4]":"entry[5]":"; \
		} \
	# Look for all confC?_FILE specifications \
	# define(confCR_FILE, -o /etc/mail/relay-domains %[^\#])dnl \
	# define(confCT_FILE, -o /etc/mail/sendmail.ct %[^\#])dnl \
	# define(confCW_FILE, -o /etc/mail/sendmail.cw %[^\#])dnl \
	/^[:space:]*define\([:space:]*`?confC[RTW]_FILE/ { \
		str = $0; \
		# Strip garbage from string \
		sub(/define\(/, "", str); \
		sub(/\)(dnl)?.*/, "", str); \
		sub(/\,/, "", str); \
		gsub(/`/, "", str); \
		gsub(/'"'"'/, "", str); \
		split(str, entry); \
		# Default elided info - format: \
		# name [ [opts] file [opts] ] \
		if (entry[2] == "-o") { \
		   entry[5] = entry[4]; \
		   entry[4] = entry[3]; \
		   entry[3] = entry[2]; \
		   } \
		else { \
		   entry[5] = entry[3]; \
		   entry[4] = entry[2]; \
		   entry[3] = "-"; \
		   entry[2] = "-"; \
		   } \
		# Save lastmost entry \
		dbs[entry[1]] = entry[2]":"entry[3]":"entry[4]":"entry[5]":"; \
		} \
	# Look for all ALIAS_FILE specifications \
	#define(ALIAS_FILE, /etc/mail/aliases.private,/etc/mail/aliases)dnl \
	/^[:space:]*define\([:space:]*`?ALIAS_FILE/ { \
		str = $0; \
		# Strip garbage from string \
		sub(/define\(/, "", str); \
		sub(/\)(dnl)?.*/, "", str); \
		gsub(/ /, "", str); \
		sub(/\,/, " ", str); \
		gsub(/`/, "", str); \
		gsub(/'"'"'/, "", str); \
		split(str, entry); \
		entry[5] = "-"; \
		entry[4] = entry[2]; \
		entry[3] = "-"; \
		entry[2] = "newaliases"; \
		# Save lastmost entry \
		dbs[entry[1]] = entry[2]":"entry[3]":"entry[4]":"entry[5]":"; \
		} \
	# Locate all non-commented FEATURE macros \
	# FEATURE(name[, type [flags] file])dnl #comment \
 	/^[:space:]*FEATURE\(.*\)/ { \
		str = $0; \
		# Strip garbage from string \
		sub(/FEATURE\(/, "", str); \
		sub(/\)(dnl)?.*/, "", str); \
		if (index(str, ",") != 0) { \
		   sub(/\,/, "", str); \
		   gsub(/`/, "", str); \
		   gsub(/'"'"'/, "", str); \
		   }; \
		split(str, entry); \
		# Ignore non-db features \
		for (x in db_features) { \
		   if (entry[1] == db_features[x]) { \
		      # Default elided info - format: \
		      # name [type [opts] file] \
		      if (entry[2] == "") { \
			 entry[5] = "-"; \
			 entry[4] = db_loc""entry[1]; \
			 entry[3] = "-o"; \
		         entry[2] = db_type; \
			 if (entry[1] == "use_ct_file" || \
			     entry[1] == "use_cw_file") { \
			     if (entry[1] == "use_ct_file") \
				str = entry[1]":"dbs["confCT_FILE"]; \
			     else \
				str = entry[1]":"dbs["confCW_FILE"]; \
			     split(str, entry, ":"); \
			     } \
			 else if (entry[1] == "access_db") \
			    entry[4] = db_loc"access"; \
			 else if (entry[1] == "uucpdomain") \
			    entry[4] = db_loc"uudomain"; \
		         } \
		      else if (entry[3] == "") { \
			 entry[5] = "-"; \
			 entry[4] = entry[2]; \
			 entry[3] = "-"; \
			 entry[2] = db_type; \
			 } \
		      else if (entry[4] == "") { \
			 entry[5] = "-"; \
			 entry[4] = entry[3]; \
			 entry[3] = "-"; \
			 }; \
		      # Save lastmost entry \
		      sub(/\.db/, "", entry[4]); \
		   dbs[entry[1]]=entry[2]":"entry[3]":"entry[4]":"entry[5]":"; \
		      }; \
		   }; \
		}; \
	END { \
		# Print the found features \
		date = ENVIRON["date"]; \
		user = ENVIRON["user"]; \
		hostname = ENVIRON["hostname"]; \
		script = ENVIRON["script"]; \
		directory = ENVIRON["directory"]; \
		print "####################################################################"; \
		print "##### This file is automatically generated -- edit at your own risk"; \
		print "#####"; \
		print "##### file: /etc/mail/databases"; \
		print "##### generated via: "script; \
		print "##### by: "user"@"hostname; \
		print "##### on: "date; \
		print "##### in: "directory; \
		print "#####"; \
		print "##### The following databases are used by sendmail"; \
		print "#####"; \
		print "####################################################################"; \
		# delete unneeded elements \
		#delete dbs["MAIL_SETTINGS_DIR"]; \
		#delete dbs["DATABASE_MAP_TYPE"]; \
		#delete dbs["confCT_FILE"]; \
		#delete dbs["confCW_FILE"]; \
		for (x in dbs) \
		    if (x != "ALIAS_FILE" && \
			x != "confUSERDB_SPEC") { \
			print x":"dbs[x]; \
			} \
		    else { \
			split(x":"dbs[x], entry, ":"); \
			pfx = entry[1]":"entry[2]":"entry[3]; \
			str = entry[4]; \
			sfx = entry[5]":"; \
			sub(/\,/, " ", str); \
			split(str, entry); \
			for (y in entry) \
				print pfx":"entry[y]":"sfx; \
			} \
		}; \
	';

#------------------------------------------------------------------------
# Make sure not using text mailertable (it doesn't work)
if (grep -q "^[[:space:]]*FEATURE(mailertable, \`text /etc/mail/mailertable')dnl" /etc/mail/sendmail.mc); then
	sed "s?FEATURE(mailertable, \`text /etc/mail/mailertable')dnl?FEATURE(mailertable)dnl?g" \
		/etc/mail/sendmail.mc > /etc/mail/sendmail.mc.new
	chown root.mail /etc/mail/sendmail.mc.new
	chmod 0664 /etc/mail/sendmail.mc.new
	mv /etc/mail/sendmail.mc.new /etc/mail/sendmail.mc
	fi;

#------------------------------------------------------------------------
# parse /etc/mail/sendmail.mc
echo "Scanning /etc/mail/sendmail.mc"
awk -- "$pattern" /etc/mail/sendmail.mc > /etc/mail/databases; \
chown root.mail /etc/mail/databases
chmod 0664 /etc/mail/databases
#cat /etc/mail/databases
#exit 0

#------------------------------------------------------------------------
# check for extant, non automagically generated Makefile and abort if found
CFGFILE="/etc/mail/Makefile"
HEADER="##### This file is automatically generated -- edit at your own risk"
if [ -s $CFGFILE ]; then
	if ! head -3 $CFGFILE | grep -q "^$HEADER" ; then
                echo "Error: the current $CFGFILE is not automatically generated."
                if [ "$1" != "force" ]; then
                        echo "Use \"$0 force\" to force (re)generation."
                        exit 1
                else
                        echo "force specified, (re)generating file anyway."
                	fi;
		fi;

	fi;

#------------------------------------------------------------------------
echo -n "Creating /etc/mail/Makefile"
makefile="/etc/mail/Makefile.new "
cat <<EOT > $makefile
#!/usr/bin/make -f
####################################################################
##### This file is automatically generated -- edit at your own risk
#####
##### file: /etc/mail/Makefile
##### generated via: $script 
##### by: $user@$hostname
##### on: $date
##### in: $directory
#####
##### Makefile for Sendmail databases
#####
####################################################################
SHELL=/bin/sh

# flag used to ensure only one newaliases command is run
newaliases_run=0

#
# all, the default target will update everything
#
all: \\
EOT
echo -n "."
#
# create all target
#
cnt=0
for file in $db_files; do
	line=$(egrep -e "^[[:space:]]*$file" /etc/mail/databases);
	while ([ "$line" != "" ]); do
		str=$(echo "$line" | cut -d "
" -f 1)
		line=$(echo "$line" | cut -d "
" -f 2)
		# Strip line back into four pieces: feature, type, opts, name
		dbfeat=$(echo "$str" | cut -d ":" -f 1)
		dbtype=$(echo "$str" | cut -d ":" -f 2)
		dbopts=$(echo "$str" | cut -d ":" -f 3)
		dbname=$(echo "$str" | cut -d ":" -f 4)
		dbregx=$(echo "$str" | cut -d ":" -f 5)
		if [ $(dirname "$dbname") = "/etc/mail" ]; then
			dbsname=$(basename "$dbname")
		else
			dbsname="$dbname"
			fi;
		if [ "$dbtype" != "-" ]; then
			cnt=`expr $cnt + 1`;
			if [ $cnt -ne 1 ]; then
				echo -e " \\" >> $makefile
				fi;
			if [ "$dbtype" = "m4" ]; then
				echo -ne "\tsendmail.cf" >> $makefile
			else
				echo -ne "\t$dbsname.db" >> $makefile
				fi;
			fi;
		done;
	done;

#------------------------------------------------------------------------
cat <<EOT >> $makefile

#
# clean target, remove sendmail.cf and databases
#
clean: 
EOT
echo -n "."
for file in $db_files; do
	line=$(egrep -e "^[[:space:]]*$file" /etc/mail/databases);
	while ([ "$line" != "" ]); do
		str=$(echo "$line" | cut -d "
" -f 1)
		line=$(echo "$line" | cut -d "
" -f 2)

		# Strip line back into four pieces: feature, type, opts, name
		dbfeat=$(echo "$str" | cut -d ":" -f 1)
		dbtype=$(echo "$str" | cut -d ":" -f 2)
		dbopts=$(echo "$str" | cut -d ":" -f 3)
		dbname=$(echo "$str" | cut -d ":" -f 4)
		dbregx=$(echo "$str" | cut -d ":" -f 5)
		if [ $(dirname "$dbname") = "/etc/mail" ]; then
			dbsname=$(basename "$dbname")
		else
			dbsname="$dbname"
			fi;
		if [ "$dbtype" != "-" ]; then
			if [ "$dbtype" = "m4" ]; then
				echo -e "\trm -f sendmail.cf" >> $makefile
			else
				echo -e "\trm -f $dbsname.db" >> $makefile
				fi;
			fi;
		done;
	done;

#------------------------------------------------------------------------
cat <<EOT >> $makefile
 
#
# Individual targets
#
EOT
echo -n "."
cnt=0
for file in $db_files; do
	line=$(egrep -e "^[[:space:]]*$file" /etc/mail/databases);
	while ([ "$line" != "" ]); do
		str=$(echo "$line" | cut -d "
" -f 1)
		line=$(echo "$line" | cut -d "
" -f 2)

		# Strip line back into four pieces: feature, type, opts, name
		dbfeat=$(echo "$str" | cut -d ":" -f 1)
		dbtype=$(echo "$str" | cut -d ":" -f 2)
		dbopts=$(echo "$str" | cut -d ":" -f 3)
		dbname=$(echo "$str" | cut -d ":" -f 4)
		dbregx=$(echo "$str" | cut -d ":" -f 5)
		if [ $(dirname "$dbname") = "/etc/mail" ]; then
			dbsname=$(basename "$dbname")
		else
			dbsname="$dbname"
			fi;
		if [ "$dbtype" != "-" ]; then
			cnt=`expr $cnt + 1`;
			if [ $cnt -ne 1 ]; then
				echo -e " " >> $makefile
				fi;
			if [ "$dbtype" = "m4" ]; then
				echo -ne "sendmail.cf:\t$dbsname" >> $makefile
				echo -e  " dialup.m4 provider.m4" >> $makefile
			else
				echo -e "$dbsname.db:\t$dbsname" >> $makefile
				fi;
			pad="\t"
			sfx=""
			if [ "$dbopts" = "-o" ]; then
				pad="\t\t"
				sfx="; \\"
				echo -e "\trm -f $dbname.db" >> $makefile
				echo -e	"\tif [ -s $dbname ] && \\" >> $makefile
				echo -e	"\t  " \
					"egrep -qe \"^[[:space:]]*[^\$$\#]\"" \
					"$dbname; then \\" \
					>> $makefile
				fi;
			case "$dbtype" in
			btree)
				echo -e $pad"makemap -d $dbtype" \
					"$dbname.db < $dbname"$sfx >> $makefile
				echo -e $pad"chown root.mail $dbname.db"$sfx \
					>> $makefile
				echo -e $pad"chmod 0664 $dbname.db"$sfx \
					>> $makefile
				;;
			dbm | btree | hash)
				echo -e $pad"makemap $dbtype" \
					"$dbname.db < $dbname"$sfx >> $makefile
				echo -e $pad"chown root.mail $dbname.db"$sfx \
					>> $makefile
				echo -e $pad"chmod 0664 $dbname.db"$sfx \
					>> $makefile
				;;
			text)
				true;
				;;
			newaliases)
				echo -ne $pad"if [ \$(newaliases_run)" \
					>> $makefile
				echo -e " -eq 0 ]; then \\" \
					>> $makefile
				echo -e $pad"\tnewaliases_run=1; \\" \
					>> $makefile
				echo -e $pad"\tnewaliases; \\" >> $makefile
				echo -e $pad"\tfi" >> $makefile
				echo -e $pad"chown root.mail $dbname.db" \
					>> $makefile
				echo -e $pad"chmod 0664 $dbname.db" \
					>> $makefile
				;;
			m4)
				cat <<EOT >> $makefile
	echo "Generating /etc/mail/sendmail.cf ..."
	m4 \\
		/usr/share/sendmail/sendmail.cf/m4/cf.m4 \\
		/etc/mail/sendmail.mc \\
		> /etc/mail/sendmail.cf.new \\
		2> /etc/mail/sendmail.cf.errors
	chown root.mail /etc/mail/sendmail.cf.new
	chmod 0644 /etc/mail/sendmail.cf.new
	if [ ! -s /etc/mail/sendmail.cf.errors ]; then \\
		rm /etc/mail/sendmail.cf.errors; \\
	else \\
		cat /etc/mail/sendmail.cf.errors; \\
		echo ""; \\
		fi;
	# Can't tell if the errors are fatal or not ;-{
	mv -f /etc/mail/sendmail.cf.new /etc/mail/sendmail.cf;
EOT
				;;
			*)
				echo -e $pad"# $dbtype map not done herein" \
					>> $makefile
				;;
			esac
			if [ "$dbopts" = "-o" ]; then
				echo -e "\t\tfi;" >> $makefile
				fi;	
			fi;
		done;
	done;

cat <<EOT >> $makefile
#
# Optional targets... Will be touched if they don't exist.
#
EOT
echo -n "."
cnt=0
for file in $db_files; do
	line=$(egrep -e "^[[:space:]]*$file" /etc/mail/databases);
	while ([ "$line" != "" ]); do
		str=$(echo "$line" | cut -d "
" -f 1)
		line=$(echo "$line" | cut -d "
" -f 2)

		# Strip line back into four pieces: feature, type, opts, name
		dbfeat=$(echo "$str" | cut -d ":" -f 1)
		dbtype=$(echo "$str" | cut -d ":" -f 2)
		dbopts=$(echo "$str" | cut -d ":" -f 3)
		dbname=$(echo "$str" | cut -d ":" -f 4)
		dbregx=$(echo "$str" | cut -d ":" -f 5)
		if [ $(dirname "$dbname") = "/etc/mail" ]; then
			dbsname=$(basename "$dbname")
		else
			dbsname="$dbname"
			fi;
	
		if [ "$dbopts" = "-o" ]; then
			cnt=`expr $cnt + 1`;
			if [ $cnt -ne 1 ]; then
				echo -e " " >> $makefile
				fi;
			echo -e "$dbsname:" >>$makefile
			echo -e "\ttouch $dbname" >>$makefile
			echo -e "\tchmod 0664 $dbname" >>$makefile
			echo -e "\tchown root.mail $dbname" >>$makefile
			fi;

		done;
	done;

cat <<EOT >> $makefile

#
# A few special targets that may not exists...
#

# dialup.m4 - created/updated by /etc/ppp/ip-up.d/sendmail
#             used to provide the true, hopefully resolvable
#	      hostname (from the ISP nameservers)
#         
#             To activate, put this in /etc/mail/sendmail.mc:
#		 include(\`/etc/mail/dialup.m4')dnl
dialup.m4:
	touch /etc/mail/dialup.m4
	chmod 0664 /etc/mail/dialup.m4
	chown root.mail /etc/mail/dialup.m4

# provider.m4 - created by the user, or /etc/ppp/ip-up.d/sendmail
#               used to provide ISP information (masquerade_as, etc.)
#               If its a link, it will *not* be overwritten
#
#	        To activate, put this in /etc/mail/sendmail.mc:
#		   include(\`/etc/mail/provider.m4')dnl
provider.m4:
	touch /etc/mail/provider.m4
	chmod 0664 /etc/mail/provider.m4
	chown root.mail /etc/mail/provider.m4

EOT
echo -n "."

echo " done."

#------------------------------------------------------------------------
chown root.mail $makefile
chmod 0770 $makefile
mv $makefile /etc/mail/Makefile
exit 0
