#!/bin/sh

#%# Copyright (C) 2014 Christoph Biedl <debian.axhn@manchmal.in-ulm.de>
#%# License: GPL-2.0-only

set -e

. gettext.sh
export TEXTDOMAIN=debian-security-support

VERSION='2015.07.11'

LIST=
NOHEADING=
STATUSDB_FILE=
TYPE=

NAME="$(basename "$0")"

TEMP=$( \
    getopt \
    --options V \
    --long list:,no-heading,semaphore:,status-db:,type:,version,Version \
    -n "$NAME" \
    -- "$@"
)

if [ $? != 0 ] ; then
    gettext "Failed to parse the command line parameters"; echo
    exit 1
fi

eval set -- "$TEMP"

while true ; do
    case "$1" in
        -V|--version|--Version)
            eval_gettext "\$name version \$VERSION"; echo
            exit 0
            ;;
        --list)
            LIST="$2"
            shift 2
            ;;
        --no-heading)
            NOHEADING='--no-heading'
            shift
            ;;
        --semaphore|--status-db)
            # --semaphore is supported for a transitional period
            STATUSDB_FILE="$2"
            shift 2
            ;;
        --type)
            TYPE="$2"
            shift 2
            ;;
        --)
            shift ;
            break
            ;;
        *)
            gettext 'E: Internal error'; echo
            exit 1
            ;;
    esac
done

case "$TYPE" in
'')
    if [ -z "$LIST" ] ; then
        $0 --type ended --list /usr/share/debian-security-support/security-support-ended --status-db "$STATUSDB_FILE" $NOHEADING
        echo
        $0 --type limited --list /usr/share/debian-security-support/security-support-limited --status-db "$STATUSDB_FILE" $NOHEADING
        exit 0
    fi
    gettext 'E: Need a --type if --list is given'; echo
    exit 1
    ;;
ended)
    [ "$LIST" ] || LIST=/usr/share/debian-security-support/security-support-ended
    ;;
limited)
    [ "$LIST" ] || LIST=/usr/share/debian-security-support/security-support-limited
    ;;
*)
    eval_gettext "E: Unknown --type '\$TYPE'"; echo
    exit 1
    ;;
esac

# exit silently if there's no list
if [ ! -f "$LIST" ] ; then
    exit 0
fi

# Get dpkg version for dpkg-query --showformat string

DPKG_VERSION="$(
    dpkg-query -f '${Version}' -W dpkg
)"

SHOWFORMAT=
if [ "$DPKG_VERSION" ] && dpkg --compare-versions "$DPKG_VERSION" '<<' 1.16 ; then
    # squeeze, does not support binary:Package
    SHOWFORMAT='${Status}\t${Package}\t${Version}\t${Source}\n'
else
    if [ -z "$DPKG_VERSION" ] ; then
        gettext "E: Cannot detect dpkg version, assuming wheezy or newer"; echo
    fi
    SHOWFORMAT='${Status}\t${binary:Package}\t${Version}\t${Source}\n'
fi


TEMPDIR="$(mktemp --tmpdir --directory debian-security-support.XXXXX)"
trap "rm -rf '$TEMPDIR'" 0

# Get list of installed packages
INSTALLED_LIST="$TEMPDIR/installed"

LC_ALL=C dpkg-query --show --showformat "$SHOWFORMAT" |
awk '($1=="install"){print}' |
awk -F'\t' '{if($4==""){print $2"\t"$3"\t"$2}else{print $2"\t"$3"\t"$4}}' >"$INSTALLED_LIST"

# Create intersection
LEFT="$TEMPDIR/left"
RIGHT="$TEMPDIR/right"
INTERSECTION_LIST="$TEMPDIR/intersection"
awk -F'\t' '{print $3}' "$INSTALLED_LIST" | LC_ALL=C sort -u >"$LEFT"
grep -v '^#' "$LIST" | LC_ALL=C sort | awk '{print $1}' >"$RIGHT"

LC_ALL=C comm -12 "$LEFT" "$RIGHT" >"$INTERSECTION_LIST"
if [ ! -s "$INTERSECTION_LIST" ] ; then
    # nothing to do
    exit 0
fi

TD="$TEMPDIR/$TYPE"
mkdir -p "$TD"

cat "$INTERSECTION_LIST" | while read SRC_NAME ; do
    IFS="$(printf '\nx')"
    IFS="${IFS%x}"
    LINE="$(awk '($1=="'"$SRC_NAME"'"){print}' "$LIST" | head -1)"
    case "$TYPE" in
        ended)
            ALERT_VERSION="$(echo "$LINE" | awk '{print $2}')"
            ALERT_WHEN="$(echo "$LINE" | awk '{print $3}')"
            ALERT_WHY="$(
                echo "$LINE" |
                awk '{
                    $0 = substr ($0, index ($0, $3) + length ($3) + 1);
                    gsub (/^[ \t]+/,"");
                    print
                }'
            )"
            ;;
        limited)
            ALERT_VERSION=
            ALERT_WHEN=
            ALERT_WHY="$(
                echo "$LINE" |
                awk '{
                    $0 = substr ($0, index ($0, $1) + length ($1) + 1);
                    gsub (/^[ \t]+/,"");
                    print
                }'
            )"
            ;;
    esac
    unset IFS

    awk '($3=="'"$SRC_NAME"'"){print $1" "$2}' "$INSTALLED_LIST" | \
    while read BIN_NAME BIN_VERSION ; do
        if \
            [ -z "$ALERT_VERSION" ] ||
            [ "$BIN_VERSION" = "$ALERT_VERSION" ] ||
            dpkg --compare-versions "$BIN_VERSION" '<=' "$ALERT_VERSION"
        then
            # need to alert, but check status db first
            TOKEN="$BIN_NAME/$BIN_VERSION"
            if [ "$STATUSDB_FILE" ] && [ -f "$STATUSDB_FILE" ]; then
                if grep -qFx "$TOKEN" "$STATUSDB_FILE" ; then
                    continue
                fi
            fi
            echo "$BIN_NAME $BIN_VERSION" >>"$TD/$SRC_NAME.bin"
            echo "$ALERT_VERSION" >"$TD/$SRC_NAME.version"
            echo "$ALERT_WHEN" >"$TD/$SRC_NAME.when"
            echo "$ALERT_WHY" >"$TD/$SRC_NAME.why"
            if [ "$STATUSDB_FILE" ] ; then
                # add to status db, remove any older entries
                if [ -f "$STATUSDB_FILE" ]; then
                    TEMPFILE="$(mktemp --tmpdir="$(dirname "$STATUSDB_FILE")")"
                    awk -F/ '($1!="'"$BIN_NAME"'"){print}' \
                        <"$STATUSDB_FILE" >"$TEMPFILE"
                    mv "$TEMPFILE" "$STATUSDB_FILE"
                fi
                echo "$TOKEN" >>"$STATUSDB_FILE"
            fi  # maintain status db
        fi # package BIN_NAME's version is not supported
    done # read binary name and version for matching source name
done # each source package from intersection

if [ $(find "$TD" -type f | wc -l) -eq 0 ] ; then
    # nothing to do
    exit 0
fi

if [ -z "$NOHEADING" ] ; then
    case "$TYPE" in
        ended)
            gettext \
"Ended security support for one or more packages

Unfortunately, it has been necessary to end security support for some
packages before the end of the regular security maintenance life cycle.

The following packages found on this system are affected by this:"
            echo
            ;;
        limited)
            gettext \
"Limited security support for one or more packages

Unfortunately, it has been necessary to limit security support for some
packages.

The following packages found on this system are affected by this:"
            echo
            ;;
    esac
fi
for f in $(find "$TD" -type f -name '*.bin' | sort) ; do
    SRC_NAME="$(basename "$f" | sed -e 's/\.bin$//')"
    ALERT_VERSION="$(cat "$TD/$SRC_NAME.version")"
    ALERT_WHEN="$(cat "$TD/$SRC_NAME.when")"
    ALERT_WHY="$(cat "$TD/$SRC_NAME.why")"
    echo
    case "$TYPE" in
        ended)
            eval_gettext "* Source:\$SRC_NAME, ended on \$ALERT_WHEN at version \$ALERT_VERSION"; echo
            ;;
        limited)
            eval_gettext "* Source:\$SRC_NAME"; echo
            ;;
    esac
    if [ "$ALERT_WHY" ] ; then
        eval_gettext "  Details: \$ALERT_WHY"; echo
    fi
    if [ $(wc -l <"$f") -eq 1 ] ; then
        gettext "  Affected binary package:"; echo
    else
        gettext "  Affected binary packages:"; echo
    fi
    cat "$f" | while read BIN_NAME BIN_VERSION ; do
        eval_gettext "  - \$BIN_NAME (installed version: \$BIN_VERSION)"; echo
    done
done

exit 0
