#!/bin/bash

# Copyright (c) 2001 Anthony Towns <ajt@debian.org>
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 2 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU General Public License for more details.

rm -rf /tmp/apt-release-check
mkdir /tmp/apt-release-check || exit 1
cd /tmp/apt-release-check

>OK
>MISSING
>NOCHECK
>BAD

arch=`dpkg --print-installation-architecture`

am_root () {
	[ `id -u` -eq 0 ]
}

get_md5sumsize () {
	cat "$1" | awk '/^MD5Sum:/,/^SHA1:/' | 
	  MYARG="$2" perl -ne '@f = split /\s+/; if ($f[3] eq $ENV{"MYARG"}) { print "$f[1] $f[2]\n"; exit(0); }'
}

checkit () {
	local FILE="$1"
	local LOOKUP="$2"

	Y="`get_md5sumsize Release "$LOOKUP"`"
	Y="`echo "$Y" | sed 's/^ *//;s/  */ /g'`"

	if [ ! -e "/var/lib/apt/lists/$FILE" ]; then
		if [ "$Y" = "" ]; then
			# No file, but not needed anyway
			echo "OK"
			return
		fi
		echo "$FILE" >>MISSING
		echo "MISSING $Y"
		return
	fi
	if [ "$Y" = "" ]; then
		echo "$FILE" >>NOCHECK
		echo "NOCHECK"
		return
	fi
	X="`md5sum < /var/lib/apt/lists/$FILE | cut -d\  -f1` `wc -c < /var/lib/apt/lists/$FILE`"
	X="`echo "$X" | sed 's/^ *//;s/  */ /g'`"
	if [ "$X" != "$Y" ]; then
		echo "$FILE" >>BAD
		echo "BAD"
		return
	fi
	echo "$FILE" >>OK
	echo "OK"
}

echo
echo "Checking sources in /etc/apt/sources.list:"
echo "~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~"
echo
(echo "You should take care to ensure that the distributions you're downloading"
echo "are the ones you think you are downloading, and that they are as up to"
echo "date as you would expect (testing and unstable should be no more than"
echo "two or three days out of date, stable-updates no more than a few weeks"
echo "or a month)."
) | fmt
echo

cat /etc/apt/sources.list | 
  sed 's/^ *//' | grep '^[^#]' |
  while read ty url dist comps; do
	if [ "${url%%:*}" = "http" -o "${url%%:*}" = "ftp" ]; then
		baseurl="${url#*://}"
	else
		continue
	fi

	echo "Source: ${ty} ${url} ${dist} ${comps}"
	
	rm -f Release Release.gpg
	lynx -reload -dump "${url}/dists/${dist}/Release" >/dev/null 2>&1
	wget -q -O Release "${url}/dists/${dist}/Release"

	if ! grep -q '^' Release; then
		echo "  * NO TOP-LEVEL Release FILE"
		>Release
	else
		origline=`sed -n 's/^Origin: *//p' Release | head -1`
		lablline=`sed -n 's/^Label: *//p' Release | head -1`
		suitline=`sed -n 's/^Suite: *//p' Release | head -1`
		codeline=`sed -n 's/^Codename: *//p' Release | head -1`
		dateline=`grep "^Date:" Release | head -1`
		dscrline=`grep "^Description:" Release | head -1`
		echo "  o Origin: $origline/$lablline"
		echo "  o Suite: $suitline/$codeline"
		echo "  o $dateline"
		echo "  o $dscrline"

		if [ "${dist%%/*}" != "$suitline" -a "${dist%%/*}" != "$codeline" ]; then
			echo "  * WARNING: asked for $dist, got $suitline/$codeline"
		fi

		lynx -reload -dump "${url}/dists/${dist}/Release.gpg" >/dev/null 2>&1
		wget -q -O Release.gpg "${url}/dists/${dist}/Release.gpg"
		
		gpgv --status-fd 3 Release.gpg Release 3>&1 >/dev/null 2>&1 | sed -n "s/^\[GNUPG:\] //p" | (okay=0; err=""; while read gpgcode rest; do
			if [ "$gpgcode" = "GOODSIG" ]; then
			    if [ "$err" != "" ]; then
				echo "  * Signed by ${err# } key: ${rest#* }"
			    else
			        echo "  o Signed by: ${rest#* }"
			        okay=1
			    fi
			    err=""
			elif [ "$gpgcode" = "BADSIG" ]; then
			    echo "  * BAD SIGNATURE BY: ${rest#* }"
			    err=""
			elif [ "$gpgcode" = "ERRSIG" ]; then
			    echo "  * COULDN'T CHECK SIGNATURE BY KEYID: ${rest%% *}"
			    err=""
			elif [ "$gpgcode" = "SIGREVOKED" ]; then
			    err="$err REVOKED"
			elif [ "$gpgcode" = "SIGEXPIRED" ]; then
			    err="$err EXPIRED"
			fi
		    done
		    if [ "$okay" != 1 ]; then
			echo "  * NO VALID SIGNATURE"
			>Release
		    fi)
	fi
	okaycomps=""
	for comp in $comps; do
		if [ "$ty" = "deb" ]; then
			X=$(checkit "`echo "${baseurl}/dists/${dist}/${comp}/binary-${arch}/Release" | sed 's,//*,_,g'`" "${comp}/binary-${arch}/Release")
			Y=$(checkit "`echo "${baseurl}/dists/${dist}/${comp}/binary-${arch}/Packages" | sed 's,//*,_,g'`" "${comp}/binary-${arch}/Packages")
			if [ "$X $Y" = "OK OK" ]; then
				okaycomps="$okaycomps $comp"
			else
				echo "  * PROBLEMS WITH $comp ($X, $Y)"
			fi
		elif [ "$ty" = "deb-src" ]; then
			X=$(checkit "`echo "${baseurl}/dists/${dist}/${comp}/source/Release" | sed 's,//*,_,g'`" "${comp}/source/Release")
			Y=$(checkit "`echo "${baseurl}/dists/${dist}/${comp}/source/Sources" | sed 's,//*,_,g'`" "${comp}/source/Sources")
			if [ "$X $Y" = "OK OK" ]; then
				okaycomps="$okaycomps $comp"
			else
				echo "  * PROBLEMS WITH component $comp ($X, $Y)"
			fi
		fi
	done
	[ "$okaycomps" = "" ] || echo "  o Okay:$okaycomps"
	echo
  done

echo "Results"
echo "~~~~~~~"
echo

allokay=true

cd /tmp/apt-release-check
diff <(cat BAD MISSING NOCHECK OK | sort) <(cd /var/lib/apt/lists && find . -type f -maxdepth 1 | sed 's,^\./,,g' | grep '_' | sort) | sed -n 's/^> //p' >UNVALIDATED

cd /tmp/apt-release-check
if grep -q ^ UNVALIDATED; then
    allokay=false
    (echo "The following files in /var/lib/apt/lists have not been validated."
    echo "This could turn out to be a harmless indication that this script"
    echo "is buggy or out of date, or it could let trojaned packages get onto"
    echo "your system."
    ) | fmt
    echo
    sed 's/^/    /' < UNVALIDATED
    echo
fi

if grep -q ^ BAD; then
    allokay=false
    (echo "The contents of the following files in /var/lib/apt/lists does not"
    echo "match what was expected. This may mean these sources are out of date,"
    echo "that the archive is having problems, or that someone is actively"
    echo "using your mirror to distribute trojans."
    if am_root; then 
        echo "The files have been renamed to have the extension .FAILED and"
        echo "will be ignored by apt."
        cat BAD | while read a; do
            mv /var/lib/apt/lists/$a /var/lib/apt/lists/${a}.FAILED
        done
    fi) | fmt
    echo
    sed 's/^/    /' < BAD
    echo
fi

if grep -q ^ MISSING; then
    allokay=false
    (echo "The following files from /var/lib/apt/lists were missing. This"
    echo "may cause you to miss out on updates to some vulnerable packages."
    ) | fmt
    echo
    sed 's/^/    /' < MISSING
    echo
fi

if grep -q ^ NOCHECK; then
    allokay=false
    (echo "The contents of the following files in /var/lib/apt/lists could not"
    echo "be validated due to the lack of a signed Release file, or the lack"
    echo "of an appropriate entry in a signed Release file. This probably"
    echo "means that the maintainers of these sources are slack, but may mean"
    echo "these sources are being actively used to distribute trojans."
    if am_root; then 
        echo "The files have been renamed to have the extension .FAILED and"
        echo "will be ignored by apt."
        cat NOCHECK | while read a; do
            mv /var/lib/apt/lists/$a /var/lib/apt/lists/${a}.FAILED
        done
    fi) | fmt
    echo
    sed 's/^/    /' < NOCHECK
    echo
fi

if $allokay; then
    echo 'Everything seems okay!'
    echo
fi

rm -rf /tmp/apt-release-check