#!/bin/bash

#%# 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='2014.04.06'

LIST=
DPKG=dpkg
DPKG_QUERY=dpkg-query
HEADING=1
SEMAPHORE_FILE=
TYPE=

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

TEMP=$( \
    getopt \
    --options V \
    --long dpkg:,dpkg-query:,list:,no-heading,semaphore:,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
            ;;
        --dpkg)
            DPKG="$2"
            shift 2
            ;;
        --dpkg-query)
            DPKG_QUERY="$2"
            shift 2
            ;;
        --list)
            LIST="$2"
            shift 2
            ;;
        --no-heading)
            HEADING=
            shift
            ;;
        --semaphore)
            SEMAPHORE_FILE="$2"
            shift 2
            ;;
        --type)
            TYPE="$2"
            shift 2
            ;;
        --)
            shift ;
            break
            ;;
        *)
            gettext 'E: Internal error'; echo
            exit 1
            ;;
    esac
done

if [ -z "$LIST" ] ; then
    $0 --type ended --list /usr/share/debian-security-support/security-support-ended
    echo
    $0 --type limited --list /usr/share/debian-security-support/security-support-limited
    exit 0
fi

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

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

DPKG_VERSION=$(
    LC_MESSAGES=C $DPKG --version |
    grep 'program version' |
    head -1 | sed -e 's/^.*program version \([^ ]*\).*$/\1/'
)

SHOWFORMAT=
if [ "$DPKG_VERSION" ] && dpkg --compare-versions "$DPKG_VERSION" '<<' 1.16 ; then
    # squeeze, does not support binary:Package
    SHOWFORMAT='${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='${binary:Package}\t${Version}\t${Source}\n'
fi


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

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

LANG=C $DPKG_QUERY --show --showformat "$SHOWFORMAT" |
awk -F'\t' '{if($3==""){print $1"\t"$2"\t"$1}else{print}}' >"$INSTALLED_LIST"

# Create intersection
INTERSECTION_LIST="$TEMPDIR/intersection"
comm -12 \
    <(awk -F'\t' '{print $3}' "$INSTALLED_LIST" | sort -u) \
    <(grep -v '^#' "$LIST" | sort | awk '{print $1}') \
        >"$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=$'\n'
    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 semaphore file first
            TOKEN="$BIN_NAME/$BIN_VERSION"
            if [ "$SEMAPHORE_FILE" ] && [ -f "$SEMAPHORE_FILE" ]; then
                if grep -qFx "$TOKEN" "$SEMAPHORE_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 [ "$SEMAPHORE_FILE" ] ; then
                # add to semaphores, remove any older entries
                if [ -f "$SEMAPHORE_FILE" ]; then
                    TEMPFILE="$(mktemp --tmpdir="$(dirname "$SEMAPHORE_FILE")")"
                    awk -F/ '($1!="'"$BIN_NAME"'"){print}' \
                        <"$SEMAPHORE_FILE" >"$TEMPFILE"
                    mv "$TEMPFILE" "$SEMAPHORE_FILE"
                fi
                echo "$TOKEN" >>"$SEMAPHORE_FILE"
            fi  # maintain semaphore file
        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 [ "$HEADING" ] ; then
    case "$TYPE" in
        ended)
            gettext \
"Security support has ended for one or more packages

Unfortunately, security support for some packages needed to be stopped
before the end of the regular security maintenance life cycle.

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

Unfortunately, security support for some packages had to be limited.

The following packages found on your 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
