#!/bin/sh
# Apt configurator.
# Copyright GPL 1999-2004 by Joey Hess
set -e

# If the first parameter is "probe", do cd autoprobing on startup.
if [ "$1" = probe ]; then
	CDPROBE=1
fi

# If the second parameter is "-N", do not set seen flags before asking
# questions; this allows for preseeding.
if [ "x$2" = "x-N" ]; then
	NONINTERACTIVE=1
fi

# Where is the mirrors master list?
MIRRORLIST=/usr/lib/base-config/Mirrors.masterlist

. /usr/share/debconf/confmodule

TEXTDOMAIN=base-config
export TEXTDOMAIN

# Get the configuration from /etc/apt/apt.conf
APTGET="/usr/bin/apt-get"
APTETC="/etc/apt/"
APTCDROM="/usr/bin/apt-cdrom"
RES=`apt-config shell \
	APTGET Dir::Bin::apt-get/d \
	APTETC Dir::Etc/d \
	APTCDROM Dir::Bin::apt-cdrom/d`
eval $RES

country_i18n () {
  # conversion country name using iso_3166 table
    case "$1" in
      local)
        perl -e '
		use Locale::gettext;
		use POSIX;
		textdomain("iso_3166");
		print gettext(shift);
		' $2
      ;;
      C)
        perl -e '
		use Locale::gettext;
		use POSIX;
		textdomain("iso_3166");
		$country=shift;
		$ret= "";

		open (T, "/usr/share/zoneinfo/iso3166.tab");
		while (<T>) {
			next if /^#/;
			chomp;
			($code, $long)=split(/\t/, $_);
			if (gettext($long) eq $country) {
			    $ret = $long;
			    close T;
			}
		}
		close T;
		print $ret ? $ret : $country;
		' $2
      ;;
    esac
}

# Pass in a URI type; this function returns a list of countries
# that have mirrors in them that match the type.
country_list () {
	# translators: there MUST be a comma followed by a space at the end
	# of your translation, or things will break!
	gettext "enter information manually, "
	perl -ne '
		BEGIN {
			use Locale::gettext;
			use POSIX;
			textdomain("iso_3166");
			$type=shift;
			open (T, "/usr/share/zoneinfo/iso3166.tab");
			while (<T>) {
				next if /^#/;
				chomp;
				($code, $long)=split(/\t/, $_);
				$co{$code}=$long;
			}
			close T;
			$/="\n\n";
		}
		if (/Archive-$type:/) {
			($c)=/Country: (.*?)\s+.*\n/;
			$countries{gettext($co{$c})}=1 if $c;
		}
		END { print join(", ", sort(keys %countries)) }
	' $1 $MIRRORLIST
}

# Pass in a URI type and a country; this function returns a 
# list of mirrors of the correct type in the country.
# The list is ordered with push mirrors at the top.
# Other orderings can be added here.
mirror_list () {
	perl -ne '
		BEGIN {
			$type=shift;
			$country=shift;
			open (T, "/usr/share/zoneinfo/iso3166.tab");
			while (<T>) {
				next if /^#/;
				chomp;
				($code, $long)=split(/\t/, $_);
				if ($long eq $country) {
					$country=$code;
				        close T;
				}
			}
			close T;
			$/="\n\n";
		}
		if (/Archive-$type:/ && /Country: $country\s+.*\n/) {
			($s)=/Site: (.*?)\n/;
			($t)=/Type: (.*?)\n/;
			$rating=0;
			$rating=1 if $t=~/push/i;
			$rating=2 if $t=~/push-primary/i;
			$mirrors{$s}=$rating;
		}
		END { print join(", ",
			sort { $mirrors{$b} <=> $mirrors{$a} }
			(keys %mirrors)) }
	' $1 "$2" $MIRRORLIST
}

# Pass in the URI type and the hostname of a mirror; this returns the
# directory the debian mirror is located in on that host.
mirror_dir () {
	perl -ne '
		BEGIN { $/="\n\n"; $type=shift; $mirror=shift }
		if (/Site: $mirror/ && /Archive-$type: (.*)\n/) {
			print $1;
			exit;
		}
	' $1 $2 $MIRRORLIST
}

# After mounting a cd, call this function to scan
# it with apt-cdrom, which also adds it to the sources.list.
# The cd will then be unmounted.
scan_cd () {
	clear >/dev/tty </dev/tty
	gettext "Scanning CD, this will take a minute."
	echo
	# Touch file because apt-cdrom bails if it doesn't exist.
	touch ${APTETC}sources.list
	if $APTCDROM add --no-mount </dev/tty >/dev/tty; then
		umount /cdrom 2>/dev/null || true
		clear >/dev/tty </dev/tty
		return 0
	else
		umount /cdrom 2>/dev/null || true
		clear >/dev/tty </dev/tty
		# Apt didn't like the cdrom for some reason.
		db_input critical apt-setup/cd/bad || true
		db_go || true
		db_fset apt-setup/cd/bad seen false
		return 1
	fi
}

# This function should be called only after one cd has been successfully
# scanned. It prompts the user if there are more cd's to scan and
# scans them. Pass in the device that is known to be the cd drive.
handle_rest_cds () {
	LOOP=1
	while [ "$LOOP" ]; do
		# Make sure the cd is unmounted, we may be prompting
		# them to change cd's.
		umount /cdrom 2>/dev/null || true

		db_set apt-setup/cd/another false
		db_input high apt-setup/cd/another || true
		db_go || true
		db_fset apt-setup/cd/another seen false
		db_get apt-setup/cd/another
		if [ "$RET" = true ]; then
			while ! mount $1 /cdrom -o ro -t iso9660
			do
				db_input critical apt-setup/cd/dev || true
				if ! db_go; then
					db_fset apt-setup/cd/dev seen false
					continue 2
				fi
				db_fset apt-setup/cd/dev seen false
				db_get apt-setup/cd/dev
				CDDEV="$RET"
			done
			scan_cd || true
		else
			LOOP=''
		fi
	done
}

# This function will ask the user if they want to add another
# apt source. If so, it returns true.
add_another () {
	# Default to adding another source if all the current sources do
	# not provide at least 300 packages. If it's closer to 200, then
	# they may be using a netinst cdrom or something, and should add
	# more sources.
	PACKAGE_COUNT=$(LC_ALL=C apt-cache stats | grep -i 'Normal Packages:' | awk '{ print $3 }')
	if [ "$PACKAGE_COUNT" -ge 300 ]; then
		prio=medium
		db_set apt-setup/another false
	else
		prio=high
		db_set apt-setup/another true
	fi
	
	#  Several notes for translators:
	#  * The singular form is only there to make use of plural forms;
	#  * If package count is 0, a separate error message should be displayed.
	#  * This sentence is part of a longer paragraph, full stop is omitted here.
	#  * "${PACKAGE_COUNT}" must not be translated.
	MESSAGE=$(ngettext 'The Debian package management tool, apt, is now configured, and can install ${PACKAGE_COUNT} package' 'The Debian package management tool, apt, is now configured, and can install ${PACKAGE_COUNT} packages' $PACKAGE_COUNT | sed -e "s/\${PACKAGE_COUNT}/$PACKAGE_COUNT/")
	db_subst apt-setup/another MESSAGE $MESSAGE

	db_input $prio apt-setup/another || return 1
	if ! db_go; then
		db_fset apt-setup/another seen false
		return 1 # effectively backup to the menu.
	fi
	db_fset apt-setup/another seen false
	db_get apt-setup/another
	if [ "$RET" = true ]; then
		return 0
	else
		return 1
	fi
}

db_capb backup
db_settitle apt-setup/title

MAINLOOP=1

# First, try to do cdrom autodetection. This is so in the most common case,
# you don't have to configure anything at all.

# If /dev/cdrom exists, use it.
if [ -e /dev/cdrom ]; then
	CDDEV=/dev/cdrom
	db_set apt-setup/cd/dev "$CDDEV"
else
	# If there is a fstab entry for /cdrom or /media/cdrom0, use its device.
	CDDEV=$(grep "[[:space:]]/media/cdrom0/*[[:space:]]" /etc/fstab | awk '{print $1}')
	if [ -z "$CDDEV" ]; then
		CDDEV=$(grep "[[:space:]]/cdrom/*[[:space:]]" /etc/fstab | awk '{print $1}')
	fi
	if [ -n "$CDDEV" ] && [ -e "$CDDEV" ]; then
		db_set apt-setup/cd/dev "$CDDEV"
	fi
fi

# Don't probe for CD if preseeded to use some other uri_type.
if [ -n "$NONINTERACTIVE" ]; then
	db_fget apt-setup/uri_type seen
	if [ "$RET" = true ]; then
		db_get apt-setup/uri_type
		if [ "$RET" != "cdrom" ]; then
			CDPROBE=""
		fi
	fi
fi

if [ "$CDPROBE" ] && [ "$CDDEV" ] && [ "$RET" = false ]; then
	umount /cdrom 2>/dev/null || true
	# Try mounting the detected cd rom.
	if mount $CDDEV /cdrom -o ro -t iso9660 2>/dev/null && scan_cd; then
		handle_rest_cds $CDDEV
		MAINLOOP=''
	else
		# Unable to mount it, or the cd was bad. Make sure that cdrom
		# is the default, and just go on to ask them where they want
		# apt to install from.
		db_set apt-setup/uri_type "cdrom"
		db_subst apt-setup/uri_type note $(gettext 'You probably used a CD to install the Debian base system, but it is not currently in the drive. You should probably just insert it and select "cdrom".')
	fi
fi

while [ "$MAINLOOP" ] || add_another; do
	# Ask what source apt should install from.
	db_input critical apt-setup/uri_type || true
	if ! db_go; then
		db_fset apt-setup/uri_type seen false
		exit 30
	fi
	db_fset apt-setup/uri_type seen false
	db_get apt-setup/uri_type
	URI="$RET"
	# Clear any note that is on the uri_type, now that they've seen it.
	db_subst apt-setup/uri_type note ""

	# If they chose to use CD, there is little point in asking
	# these questions, since the CD's they insert will answer them for us.
	# Same goes if they are entering manually.
	if [ "$URI" != "cdrom" ] && [ "$URI" != "edit sources list by hand" ]; then
		db_beginblock
		db_input low mirror/suite || true
		db_input low apt-setup/non-free || true
		db_endblock
		if ! db_go; then
			db_fset mirror/suite seen false
			db_fset apt-setup/non-free seen false
			continue
		fi
		db_fset mirror/suite seen false
		db_fset apt-setup/non-free seen false
			
		# If they choose not use non-free, ask about contrib, with a
		# default of no. If they chose to use non-free, they get
		# contrib too..
		db_get apt-setup/non-free
		if [ "$RET" = false ]; then
			db_fget apt-setup/contrib seen
			if [ "$RET" = false ]; then
				db_set apt-setup/contrib false
			fi
			db_input low apt-setup/contrib || true
			if ! db_go; then
				db_fset apt-setup/contrib seen false
				continue
			fi
			db_fset apt-setup/contrib seen false
		else
			db_fget apt-setup/contrib seen
			if [ "$RET" = false ]; then
				db_set apt-setup/contrib true
			fi
		fi
	fi

	case "$URI" in
	ftp|http)
		# Ask them which country they're in.
		db_subst apt-setup/country countries "`country_list $URI`"
		db_fget apt-setup/country seen
		if [ "$RET" = "false" ]; then
			# Set default country
			db_get debian-installer/country
			COUNTRY=$(perl -e '
				$countrycode=shift;
				open (T, "/usr/share/zoneinfo/iso3166.tab");
				while (<T>) {
					next if /^#/;
					chomp;
					($code, $long)=split(/\t/, $_);
					if ($code eq $countrycode) {
						print "$long";
					        close T;
					}
				}
				close T;
		   	' $RET)
		   	if [ -n "$COUNTRY" ]; then
		   		db_set apt-setup/country "$COUNTRY"
		  	fi
		fi
		db_get apt-setup/country
		IFS=","
		db_set apt-setup/country $(country_i18n local "$RET")
		unset IFS
		db_get apt-setup/country
		db_input critical apt-setup/country || true
		if ! db_go; then
			db_fset apt-setup/country seen false
			continue
		fi
		db_fset apt-setup/country seen false

		db_get apt-setup/country
		if [ "$RET, " != "$(gettext 'enter information manually, ')" ]; then
		   IFS=","
		   db_set apt-setup/country $(country_i18n C "$RET")
		   unset IFS
		else
		   db_set apt-setup/country 'enter information manually'
		fi

		db_get apt-setup/country
		if [ "$RET" != "enter information manually" ]; then
			# Now prompt with the mirrors in the selected country.
			db_subst apt-setup/mirror mirrors "`mirror_list $URI \"$RET\"`"
			db_input critical apt-setup/mirror || true
			if ! db_go; then
				db_fset apt-setup/mirror seen false
				continue
			fi
			db_fset apt-setup/mirror seen false
			
			# Now shove the data about the mirror into some other
			# questions.
			db_get apt-setup/mirror
			MIRROR="$RET"
			db_set apt-setup/hostname $MIRROR
			db_set apt-setup/directory "`mirror_dir $URI $MIRROR`"
		else
			# They elected to enter info manually.
			# Prompt for hostname and directory the mirror is in.
			db_beginblock
			db_input critical apt-setup/hostname || true
			db_input critical apt-setup/directory || true
			db_endblock
			if ! db_go; then
				db_fset apt-setup/hostname seen false
				db_fset apt-setup/directory seen false
				continue
			fi
			db_fset apt-setup/hostname seen false
			db_fset apt-setup/directory seen false
		fi

		# Ask about a proxy if no proxy is yet defined.
		if [ "$URI" = "http" ] && [ -z "$http_proxy" ]; then
			if [ ! -e "$APTETC/apt.conf" ] || \
			   ! grep -iq 'Acquire::http::Proxy' $APTETC/apt.conf; then
				db_input high mirror/http/proxy || true
				if ! db_go; then
					db_fset mirror/http/proxy seen false
					continue
				fi
				db_fset mirror/http/proxy seen false
			fi
		fi
	;;
	cdrom)
		# We've already probed earlier to try to figure out the
		# cd device.
		LOOP=1
		LOOPCOUNT=5
		while [ "$LOOP" ]; do
			# Make sure the cd is unmounted, we may be prompting
			# them to change cd's.
			umount /cdrom 2>/dev/null || true
		
			# Prompt for the cd device if it wasn't autodetected.
			# TODO: We could give them a list of likely devices..
			#       This is unfriendly right now.
			if [ -z "$CDDEV" ]; then
				db_input critical apt-setup/cd/dev || true
				db_go || continue 2
				db_get apt-setup/cd/dev
				CDDEV="$RET"
				# Make /dev/cdrom link now, with device
				# they entered. This is for later use by
				# apt.
				if [ "$CDDEV" ] && [ "$CDDEV" != '/dev/cdrom' ]; then
					ln -sf $CDDEV /dev/cdrom
				fi
			fi
			
			# Now try to mount the cdrom.
			if mount $CDDEV /cdrom -o ro -t iso9660; then
				LOOP=""
				if scan_cd; then
					handle_rest_cds $CDDEV
					MAINLOOP=""
				fi
			else
				# Only loop LOOPCOUNT times.
				LOOPCOUNT=`expr $LOOPCOUNT - 1`
				if [ "0" -ge "$LOOPCOUNT" ]; then
					LOOP=""
				fi
				
				# Loop and prompt again for cd device.
				db_fset apt-setup/cd/dev seen false
				CDDEV=""
			fi
		done
		continue
	;;
	
	filesystem)
		# they need to have a mirror already mounted somewhere.
		URI=file
		LOOP=1
		LOOPCOUNT=5
		while [ "$LOOP" ]; do
			db_input critical apt-setup/directory || true
			if ! db_go; then
				db_fset apt-setup/directory seen false
				continue 2
			fi
			db_fset apt-setup/directory seen false
			db_get apt-setup/directory
			
			# A very simple mirror sanity check.
			if [ ! -d "$RET" ]; then
				db_input critical apt-setup/baddir || true
				db_go || true
				db_fset apt-setup/baddir seen false
			elif [ ! -d "$RET/dists" ]; then
				db_input critical apt-setup/not-mirror || true
				db_go || true
				db_fset apt-setup/not-mirror seen false
			else
				LOOP=''
			fi
			
			# Only try LOOPCOUNT times.
			LOOPCOUNT=`expr $LOOPCOUNT - 1`
			if [ "0" -ge "$LOOPCOUNT" ]; then
				LOOP=""
			fi
		done
	;;
	*)
		touch ${APTETC}sources.list
		cp -f ${APTETC}sources.list ${APTETC}sources.list.bak
		# Just run an editor on the sources.list file, then
		# check the result. If it fails, show the problem and loop.
		if [ -z "$EDITOR" ]; then
			EDITOR=/usr/bin/editor
		fi
		EDITOK=""
		while [ ! "$EDITOK" ]; do
			touch ${APTETC}sources.list
			$EDITOR ${APTETC}sources.list >/dev/tty </dev/tty
			tempfile=`tempfile`
			clear >/dev/tty </dev/tty
			gettext "Testing apt sources..."
			echo
			if $APTGET -o APT::Get::List-Cleanup=false -o Dir::Etc::sourcelist=${APTETC}sources.list update 2>$tempfile; then
				clear >/dev/tty </dev/tty
				EDITOK=1
			else
				clear >/dev/tty </dev/tty
				db_subst apt-setup/badedit apt_error `tr '\n' ' ' < $tempfile`
				db_input critical apt-setup/badedit || true
				db_go || true
				db_fset apt-setup/badedit seen false
				db_get apt-setup/badedit
				if [ "$RET" != "edit" ]; then
					mv -f ${APTETC}sources.list.bak ${APTETC}sources.list
					continue 2
				fi
			fi
		done
		MAINLOOP=""
		rm -f ${APTETC}sources.list.bak
		continue
	esac

	# The temporary file to use as sources.list for testing
	# new items.
	tmpsources=`tempfile`

	db_get apt-setup/hostname
	HOST="$RET"
	db_get apt-setup/directory
	DIR="$RET"
	db_get mirror/suite
	DIST="$RET"
	
	db_get apt-setup/non-free
	if [ "$RET" = true ]; then
		NONFREE=" non-free"
	else
		NONFREE=""
	fi
	db_get apt-setup/contrib
	if [ "$RET" = true ]; then
		CONTRIB=" contrib"
	else
		CONTRIB=""
	fi
	if [ "$URI" = ftp ] || [ "$URI" = http ]; then
		SEP=//
	else
		SEP=""
	fi
	if [ "$URI" = file ]; then
		HOST=""
	fi

	echo "deb $URI:$SEP$HOST$DIR $DIST main$NONFREE$CONTRIB" >> $tmpsources
	SRCENTRY="deb-src $URI:$SEP$HOST$DIR $DIST main$NONFREE$CONTRIB"

	# If there is a http proxy, make apt use it temporarily.
	db_get mirror/http/proxy
	if [ "$RET" ]; then
		PROXY_OPTS="-o Acquire::http::Proxy=\"$RET\""
	else
		PROXY_OPTS=""
	fi

	# As a final sanity check, run apt-get update, and catch the
	# return code and errors.
	tempfile=`tempfile`
	clear >/dev/tty </dev/tty
	gettext "Testing apt sources..."
	echo
	if ! $APTGET $PROXY_OPTS -o APT::Get::List-Cleanup=false -o Dir::Etc::sourcelist=$tmpsources update 2>$tempfile; then
		clear >/dev/tty </dev/tty
		# Show the user the error message and loop.
		db_subst apt-setup/badsource apt_error `tr '\n' ' ' < $tempfile`
		db_input critical apt-setup/badsource || true
		db_go || true
		db_fset apt-setup/badsource seen false
		rm -f $tempfile $tmpsources
		continue
	fi
	# Success, so add the entry to the real sources.list
	touch ${APTETC}sources.list
	echo "" >> ${APTETC}sources.list
	cat $tmpsources >> ${APTETC}sources.list
	MAINLOOP=""
	
	# Success, so add proxy information if not already present.
	db_get mirror/http/proxy
	if [ "$RET" ]; then
		touch $APTETC/apt.conf
		if ! grep -iq 'Acquire::http::Proxy' $APTETC/apt.conf; then
			echo "Acquire::http::Proxy \"$RET\";" >> $APTETC/apt.conf
		fi
	fi
	
	# We have a deb-src line to add too. Test it, see if it works. If not,
	# don't fail, just don't add it.
	echo "$SRCENTRY" > $tmpsources
	if $APTGET -o APT::Get::List-Cleanup=false -o Dir::Etc::sourcelist=$tmpsources update 2>/dev/null; then
		echo "$SRCENTRY" >> ${APTETC}sources.list
	fi
	clear >/dev/tty </dev/tty

	rm -f $tempfile $tmpsources
done

# Finally, see about adding a security.debian.org entry. Deal with these
# situations:
# * already have it in sources.list, uncommented: don't ask about it
# * unstable does not need security.debian.org, so try not to even ask
#   if that's all they have in sources.list
# * installing from CD; no network connectivity: add it, commented out
# * adding it when the user has picked non-free and/or contrib at least
#   some of the time: include those
# * adding it when the user has not: don't include those

if apt-cache policy | grep "o=Debian,a=" | grep -qv 'a=unstable' && \
   ! grep -q '^[^#]*security.debian.org' ${APTETC}sources.list; then
	# Figure out what line to add (we're definitely going to add something,
	# even if it's just a comment).
	#
	# To determine if non-free and contrib should be included, grep
	# the file to see if they are listed in it.
	DISTS="main"
	for dist in contrib non-free; do
		if grep -q '^[^#]*'$dist ${APTETC}sources.list; then
			DISTS="$DISTS $dist"
		fi
	done

	# Figure out which suite to use.
	if apt-cache policy | grep "o=Debian,a=" | grep -q 'a=testing'; then
	    SUITE="testing"
	else
	    SUITE="stable"
	fi

	LINE="http://security.debian.org/ $SUITE/updates $DISTS"

	COMMENT="# "

	# Now ask if they want the entry to be added.
	db_input medium apt-setup/security-updates || true
	db_go || true
	db_fset apt-setup/security-updates seen false
	db_get apt-setup/security-updates
	if [ "$RET" = true ]; then
		# Test it.
		tmpsources=`tempfile`
		echo "deb $LINE" > $tmpsources
		clear >/dev/tty </dev/tty
		gettext "Testing apt sources..."
		echo
		tempfile=`tempfile`
		if ! $APTGET -o APT::Get::List-Cleanup=false -o Dir::Etc::sourcelist=$tmpsources update 2>$tempfile; then
			# Display failure message
			clear >/dev/tty </dev/tty
			db_subst apt-setup/security-updates-failed apt_error `tr '\n' ' ' < $tempfile`
			db_input critical apt-setup/security-updates-failed || true
			db_go || true
			db_fset apt-setup/security-updates-failed seen false
		else
			# Success. Don't comment them out.
			COMMENT=""
			# TODO: test and add deb-src too.
		fi
	
		rm -f $tempfile $tmpsources
	fi

	# Now add the entry, commented out or not. (Don't add the commented
	# version if a commented version already exists.)
	if [ -z "$COMMENT" ] || ! grep -q '#.*security.debian.org' ${APTETC}sources.list; then
		echo "" >>${APTETC}sources.list
		echo "${COMMENT}deb $LINE" >>${APTETC}sources.list
	fi	
fi

