#!/bin/sh

. /usr/share/debconf/confmodule

log() {
	logger -t net-retriever "$@"
}
error() {
	log "error: $@"
	exit 1
}

db_get mirror/protocol
protocol="$RET"
db_get mirror/$protocol/hostname
hostname="$RET"
db_get mirror/$protocol/directory
directory="$RET"
db_get apt-setup/_DEVEL_/repository && {
	de_debed="${RET#deb }"
	dev_url="${de_debed%% *}"
}

keyring=/usr/share/keyrings/archive.gpg
if [ -n "$dev_url" ]; then
	dev_keyring=${keyring%/*}/_DEVEL_.gpg
	db_get apt-setup/_DEVEL_/key
	printf '%s' "${RET#base64://}" | base64 -d > ${dev_keyring}
fi

fetch() {
	case $1 in
	    @@devel@@*)
			if [ -z "$dev_url" ]; then
				error "fetch of '$1' with no 'apt-setup/_DEVEL_/repository' active -- something untoward is going on"
			fi
			log "devel: $@"
			fetch-url -c "${dev_url}/${1#@@devel@@}" "$2"
			;;
	    *)
			log "regular: $@"
			fetch-url -c "${protocol}://${hostname}${directory}/$1" "$2"
			;;
	esac
}

# Note: callers are expected to check for non-empty strings in return,
# which could indicate an unknown checksum type, or a missing foosum
# binary.
get_checksum() {
	type="$1"
	file="$2"

	case "$type" in
		SHA256) sha256sum "$file" | cut -d' ' -f1 ;;
		*)      error "Unknown checksum type $type for $file"
	esac
}

checkmatch() {
	Release="$1"
	Packages="$2"
	pkgfile="$3"
	pkgsize="$(wc -c < "$Packages" | tr -d ' ')"

	# Note: When checksum types are modified by the FTP team, make
	# sure to update both the list below and the case statement in
	# get_checksum().
	for checksumtype in SHA256; do
		pkgchecksum=$(get_checksum "$checksumtype" "$Packages")
		if [ -z "$pkgchecksum" ]; then
			error "Please report a bug: get_checksum() returned nothing"
		fi

		set -e
		sed -n "/^$checksumtype:\$/ b LOOP; b; : PRINT; /:\$/q; p; : LOOP; n; b PRINT" \
		       "$Release" | (
			found=0
			while read checksum size file; do
				if [ "$file" = "$pkgfile" ]; then
					if [ "$checksum" != "$pkgchecksum" ]; then
						error "$checksumtype mismatch for $pkgfile ($checksum != $pkgchecksum)."
					fi
					if [ "$size" != "$pkgsize" ]; then
						error "Size mismatch for $pkgfile ($size != $pkgsize)."
					fi
					found=1
				fi
			done
			if [ "$found" != 1 ]; then
				error "$pkgfile not found in $Release (for $checksumtype checksum)."
			fi
		)
		set +e
	done
}

# expand_packages()
# takes a Packages filename, its extension (and the dev_prefix)
# and applies the approapriate decompressor (or none) -- output to stdout
# dev_prefix adds a magic prefix for later use in fetch for the dev repo.
expand_packages() {
	Packages=$1   ; shift
	ext=$1        ; shift
	dev_prefix=$1 ; shift

	local ZCAT;

	case "$ext" in
		'')    ZCAT=cat ;;
		'.gz') ZCAT=zcat ;;
		'.xz') ZCAT=xzcat ;;
	esac

	if [ "@@devel@@" = "$dev_prefix" ] ; then
		$ZCAT "$Packages" | sed -e 's/^Filename: /&@@devel@@/g'
	else
		$ZCAT "$Packages"
	fi
}

read_gpg_status() {
	while read prefix keyword rest; do
		[ "$prefix" = '[GNUPG:]' ] || continue
		if [ "$keyword" = VALIDSIG ]; then
			exit 0
		fi
	done
	exit 1
}

cmd="$1"
shift

case "$cmd" in
    retrieve)
	fetch "$@"
	exit $?
	;;

    packages)
	rm -f "$1"
	touch "$1"

	# Setting codename to a suite is not very nice, but can do no harm
	if ! db_get mirror/udeb/suite || [ -z "$RET" ]; then
		if [ -f /etc/udebs-source ]; then
			RET=$(cat /etc/udebs-source)
		else
			db_get mirror/codename
		fi
	fi
	codename="$RET"

	for dev_prefix in "" ${dev_url:+@@devel@@} ; do
		Release="/tmp/${dev_prefix}net-retriever-$$-Release"
		fetch "${dev_prefix}dists/$codename/Release" "$Release" || exit $?
		# If gpgv and a keyring are installed, authentication is
		# mandatory by default.
		if type gpgv >/dev/null && [ -f "$keyring" ]; then
			if db_get debian-installer/allow_unauthenticated && [ "$RET" = true ]; then
				log "Not verifying Release signature: unauthenticated mode enabled"
			else
				if ! fetch "${dev_prefix}dists/$codename/Release.gpg" "$Release.gpg"; then
					error "dists/$codename/Release is unsigned."
				fi
				if [ -z "$dev_prefix" ]; then
					k="$keyring"
				else
					k="$dev_keyring"
				fi
				if ! log-output -t net-retriever --pass-stdout \
					gpgv --status-fd 1 --keyring "$k" \
					--ignore-time-conflict \
					"$Release.gpg" "$Release" | read_gpg_status; then
					error "Bad signature on $Release."
				fi
			fi
		else
			log "Not verifying Release signature: gpgv not available"
		fi

		ARCH=`udpkg --print-architecture`
		components="`grep ^Components: $Release | cut -d' ' -f2-`"
		ret=1
		if [ -z "$components" ]; then
			error "No components listed in $Release."
		fi
		for comp in $components; do
			for ext in '.xz' '.gz' ''; do
				pkgfile="$comp/debian-installer/binary-$ARCH/Packages$ext"
				line=`grep $pkgfile\$ $Release 2>/dev/null`
				if [ $? != 0 ]; then
					continue
				fi
				Packages="/tmp/${dev_prefix}net-retriever-$$-Packages"
				rm -f "$Packages"
				[ -z "$dev_prefix" ] || log "Checking $dev_url for DevelPackages"
				fetch "${dev_prefix}dists/$codename/$pkgfile" "$Packages" || continue
				[ -z "$dev_prefix" ] || log "Got devel Packages from dists/$codename/$pkgfile"
				checkmatch "$Release" "$Packages" "$pkgfile"
				expand_packages "$Packages" "$ext" "$dev_prefix" >> "$1"
				ret=0
				break
			done
		done
		[ "0" = "$ret" ] || exit $ret
	done
	exit $ret
	;;

    error)
	T="retriever/net/error"
	db_set "$T" "Retry"
	db_input critical "$T" || true

	if ! db_go; then
		exit 2
	fi
	db_get "$T"
	if [ "$RET" = "Retry" ]; then
		exit 0
	elif [ "$RET" = "Change mirror" ]; then
		choose-mirror || true
		exit 0
	elif [ "$RET" = Cancel ]; then
		exit 2
	fi
	;;

    *)
	# unknown or missing command
	exit 1
	;;
esac
