#!/bin/sh -e
#
# base-config configure script
#
# This script runs when a fresh debian install is first booted, to
# finish up configuring the base system. It can also be run at other times,
# although it does much less then.

. /usr/share/debconf/confmodule
db_capb 'backup'

fixtitle () {
	db_title 'Debian System Configuration'
}
fixtitle

# Check to see if we are in noninteractive mode, if so bail -- the base 
# system is probably being built; anyway, there are possible infinite loops
# below in noninteractive mode. The only way to check for noninteractive
# mode is to ask a critical question.
db_fset base-config/root-password isdefault true
db_input critical base-config/root-password || exit 0
db_clear 2>/dev/null || exit 0

# Load up dbootstrap config file, if present.
if [ -e /root/dbootstrap_settings ]; then
	. /root/dbootstrap_settings || true
	# Take whatever verbosity level was used in boot floppies, and use it.
	if [ "$VERBOSE" = yes ]; then
		db_set debconf/priority low
	elif [ "$VERBOSE" = quiet ]; then
		db_set debconf/priority critical
	fi

	# If there is a proxy setting, use that as the default.
	if [ "$HTTP_PROXY" ]; then
		db_set apt-setup/http_proxy "$HTTP_PROXY"
	fi

	# Keyboard configuration.
	# TODO: this doesn't belong here.
	if [ "$KEYBD" ]; then
		# do automatic configuration
		loadkeys /usr/share/keymaps/${KEYBD}.kmap.gz
		dumpkeys > /etc/console-tools/default.kmap
		gzip -9fv /etc/console-tools/default.kmap
	elif [ "$SERIALCONSOLE" ]; then
		# we are on serial console -- no kbd config at all
		dpkg --purge console-data console-tools console-tools-libs \
		    </dev/tty >/dev/tty || true
	elif [ ! -f /etc/console-tools/default.kmap.gz -a -f /bin/loadkeys ]; then
		# no file -- ask user
		kbdconfig </dev/tty >/dev/tty || true
		if ! loadkeys /etc/console-tools/default.kmap.gz; then
			db_input critical base-config/keymap-failed
			db_go
		fi
	fi
fi

# Use a state machine to allow jumping back to previous questions.
STATE=0

# This runs db_go, and changes the state appropriatly based on the result
go () {
	if db_go; then
		STATE=`expr $STATE + 1`
	else
		STATE=`expr $STATE - 1`
	fi
}

# Set a password, via chpasswd.
# Use perl rather than echo, to avoid the password
# showing in the process table. (However, this is normally
# only called when first booting the system, when root has no
# password at all, so that should be an unnecessary precaution.
#
# Pass in two arguments: the user and the password.
setpassword () {
	SETPASSWD_PW="$2"
	export SETPASSWD_PW

	# This is very annoying. chpasswd cannot handle generating md5
	# passwords as it is not PAM-aware. Thus, I have to work around
	# that by crypting the password myself if md5 is used.
	db_get base-config/md5
	if [ "$RET" = true ]; then
		USE_MD5=1
	else
		USE_MD5=''
	fi
	export USE_MD5
	perl -e '
		sub CreateCryptSalt {
			my $md5 = shift;

			my @valid = split(//, "./0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ");
			my ($in, $out);

			my $cryptsaltlen = ($md5 ? 8 : 2);

			open (F, "</dev/urandom") || die "No /dev/urandom found!";
			foreach (1..$cryptsaltlen) {
				read(F, $in, 1);
				$out .= $valid[ord($in) % ($#valid + 1)];
			}
			close F;
			return ($md5 ? "\$1\$$out\$" : $out);
		}
	
		open(P,"| chpasswd -e");
		print P shift().":".
			crypt($ENV{SETPASSWD_PW}, CreateCryptSalt($ENV{USE_MD5})).
			"\n";
		close P;
	' "$1"
	SETPASSWD_PW=''
	USE_MD5=''
}

# Configure and turn on ppp.
enableppp () {
	pppconfig </dev/tty >/dev/tty || true
	status=1
	while [ "$status" != "0" ]; do
		set +e
		# Note: don't redirect output/input, that makes it hang!
		# Very weird..
		pon </dev/tty >/dev/tty 2>/dev/tty 3>/dev/tty
		status=$?
		set -e
		if [ "$status" != "0" ]; then
			# See if we should retry that.
			db_fset base-config/retry-ppp isdefault true
			db_input critical base-config/retry-ppp || true
			db_go || true
			db_get base-config/retry-ppp
			if [ "$RET" = false ]; then
				status=0
			fi
		fi
	done
}

while [ "$STATE" != '5' -a "$STATE" != '-1' ]; do
	case "$STATE" in
	0)
		# Set up the password files. Shadow, md5, etc.
		db_input medium base-config/md5 || true
		if [ -x /usr/sbin/shadowconfig ]; then
			if [ -e /etc/shadow ]; then
				# Don't ask the question unless we are being
				# reconfigured.
				db_fset base-config/shadow isdefault false
			fi
			db_input medium base-config/shadow || true
		fi
		go
		
		# Turn md5 passwords on/off now, so the two passwords set
		# below can use it.
		if [ -e /etc/pam.d/passwd ]; then
			db_get base-config/md5
			for file in /etc/pam.d/passwd /etc/pam.d/login; do
				if [ "$RET" = true ]; then
					# On.
					# TODO: only modify lines that match
					# ^password.*required.*pam_(unix|ldap|pwdb).so.*
					if ! egrep -q "^password.*required.*md5" $file ; then
						sed 's/^\(password.*required.*\)/\1 md5/' \
							< $file > $file.new
						mv -f $file.new $file
					fi
				else
					# Off.
					if egrep -q "^password.*required.*md5" $file ; then
						sed 's/^\(password.*required.*\)md5/\1/' \
							< $file > $file.new
						mv -f $file.new $file
					fi
				fi
			done
		fi
	;;
	1)
		# Ensure that root has a password.
		if [ -z "`fgrep root /etc/passwd | cut -d : -f 2`" ]; then
			db_set base-config/root-password ""
			db_set base-config/root-password-again ""

			db_fset base-config/root-password isdefault true
			db_input critical base-config/root-password || true
			# Note that this runs at a slightly lower
			# priority, so it may not always be seen. If
			# it isn't, don't compare passwords.
			db_fset base-config/root-password-again isdefault true
			COMPARE_PW=''
			db_input high base-config/root-password-again \
				&& COMPARE_PW=1 || true
			go
			if [ "$STATE" = 2 ]; then
				STATE=1
			fi
			
			# Compare the two passwords, loop if not
			# identical, or if empty.
			db_get base-config/root-password
			ROOT_PW="$RET"
			db_get base-config/root-password-again
			if [ "$COMPARE_PW" -a "$ROOT_PW" != "$RET" ]; then
				db_fset base-config/password-mismatch isdefault true
				db_input critical base-config/password-mismatch
				continue
			fi
			if [ -z "$ROOT_PW" ]; then
				db_fset base-config/password-empty isdefault true
				db_input critical base-config/password-empty
				continue
			fi
			
			# Clear root password from the db, and set the
			# password.
			db_set base-config/root-password ""
			db_set base-config/root-password-again ""
			setpassword root "$ROOT_PW"
			ROOT_PW=''
			
			# Loop back through this state, and make sure it
			# worked.
			STATE=1
		else
			STATE=2
		fi
	;;
	
	2)
		# Ask if a non-root user should be made, if there is not
		# already one. Note that this check also looks for NIS entries
		# and does nothing if they exist.
		if ! egrep -q '^.*:.*:1000:' /etc/passwd && \
		   ! egrep -q '^+::::::' /etc/passwd; then
			db_fset base-config/make-user isdefault true
			db_input medium base-config/make-user || true
		fi
		go
	;;
	
	3)
		# Get password and username info for the user, and make
		# the user.
		db_get base-config/make-user
		if ! egrep -q '^.*:.*:1000:' /etc/passwd && [ "$RET" = true ]; then
			LOOP=""
			db_input critical base-config/username || true
			db_input medium base-config/user-fullname || true
			db_input critical base-config/user-password || true
			COMPARE_PW=''
			db_input high base-config/user-password-again \
				&& COMPARE_PW=1 || true
			go
			if [ "$STATE" = 4 ]; then
				STATE=3
			fi
			
			# Verify the user name, loop with message if bad.
			db_get base-config/username
			USER="$RET"
			if ! expr "$USER" : '[a-z][a-z0-9]*$' >/dev/null; then
				db_fset base-config/username isdefault true
				db_fset base-config/username-bad isdefault true
				db_input critical base-config/username-bad
				LOOP=1
			fi
			
			# Compare the two passwords, loop with message if not
			# identical, or if empty.
			db_get base-config/user-password
			USER_PW="$RET"
			db_get base-config/user-password-again
			if [ "$COMPARE_PW" -a "$USER_PW" != "$RET" ]; then
				db_set base-config/user-password ""
				db_set base-config/user-password-again ""
				db_fset base-config/password-mismatch isdefault true
				db_input critical base-config/password-mismatch
				db_fset base-config/user-password isdefault true
				db_fset base-config/user-password-again isdefault true
				LOOP=1
			fi
			if [ -z "$USER_PW" ]; then
				db_set base-config/user-password ""
				db_set base-config/user-password-again ""
				db_fset base-config/password-empty isdefault true
				db_input critical base-config/password-empty
				db_fset base-config/user-password isdefault true
				db_fset base-config/user-password-again isdefault true
				LOOP=1
			fi
			
			if [ "$LOOP" ]; then
				continue
			fi
			
			# Add the user to the database, using adduser in
			# noninteractive mode.
			db_get base-config/user-fullname
			adduser --disabled-password --gecos "$RET" "$USER" >/dev/null || true
			
			# Clear password from the db, and set the password.
			db_set base-config/user-password ""
			db_set base-config/user-password-again ""
			db_get base-config/username
			setpassword "$USER" "$USER_PW"
			USER_PW=''
		else
			STATE=4
		fi
	;;
	4)	
		# Running this block if dpkg/apt are running already is very
		# bad. So, don't run the block at all unless the package is
		# being reconfigured.
		if [ "$1" = reconfigure ]; then
			if [ "$PCMCIA" != yes ]; then
				db_input medium base-config/remove-pcmcia || true
			fi	
			db_input critical base-config/use-ppp || true
			db_go || true
			db_get base-config/use-ppp
			if [ "$RET" = true ]; then
				enableppp
			fi
			# Set up apt. This is an external program because it
			# can be useful to run it standalone. Don't set up apt
			# except for on initial reboot.
			if [ -e /root/dbootstrap_settings -a -x /usr/sbin/apt-setup ]; then
				# Coment out the shipped sources.list, since it
				# assumes they are on the network, and they
				# might not be.
				touch /etc/apt/sources.list
				if [ -e /root/dbootstrap_settings ]; then
					sed 's/^\([^#]\)/#\1/' /etc/apt/sources.list > /etc/apt/sources.list.new
					mv -f /etc/apt/sources.list.new /etc/apt/sources.list
				fi
				# Come up with a working apt sources.list.
				apt-setup probe || (STATE=3 ; fixtitle ; break)
				fixtitle
			fi
			if [ -x /usr/bin/tasksel -a -x /usr/bin/dselect ]; then
				db_input medium base-config/selection-path || true
			fi
		fi
		go
	esac
done
