#!/bin/sh
# -*- mode: shell-script; coding: utf-8 -*-
#
# distmd5sum
#
# Script to check whether a configuration file has the same MD5 sum as
# distributed in a package.
#
# Copyright (C) 2010-2012 Elmar Hoffmann
#
# 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.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
#

set -e

MD5DIR='/usr/share/elho/md5sums'

. /lib/elho/shell-tools

# get packages that contain a file with the given filename
deb_file_packages ()
{
    file="$1"

    dpkg --search "${file}" 2>/dev/null \
	| sed --expression='/^\(local \)\?diversion /d;s/^\([^:]\+\):.*$/\1/;s/,[[:space:]]*/\n/g' \
	| uniq
}

# get list of conffiles and their MD5 sums of given package, formatted on
# individual lines to be searched with fixed strings, avoiding any regex
# injection
deb_conffiles_md5sums ()
{
    package="$1"

    dpkg-query --showformat='${Conffiles}\n' --show "${package}" \
	| sed --quiet --expression='s/^[[:space:]]\+\([^[:space:]]\+\)[[:space:]]\+\([0-9a-f]\{32\}\)$/F:\1\nM:\2/p'
}

# reformat given md5sum(1) file to list of files and their MD5 sums on
# individual lines to be searched with fixed strings, avoiding any regex
# injection
reformat_md5sums ()
{
    md5file="$1"

    sed --quiet --expression='s/^\([0-9a-f]\{32\}\)[[:space:]]\+\([^[:space:]]\+\)$/F:\2\nM:\1/p' "${md5file}"
}

# find MD5 sum(s) of given file in output as formatted by
# deb_conffiles_md5sums and reformat_md5sums functions
file_md5sum ()
{
    file="$1"

    grep --after-context=1 --fixed-strings --line-regexp "F:${file}" \
	| sed --quiet --expression='s/^M://p'
}

check_md5sums ()
{
    package="$1"
    file="$2"
    md5sums="$3"

    for md5 in ${md5sums}; do
	debug "MD5 sum in package '${package}': ${md5}"
	if echo "${md5}  ${file}" | md5sum --check --status; then
	    verbose "'${file}' has the same MD5 sum as distributed in" \
		"package '${package}'."
	    exit 0
	fi
    done
}

usage ()
{
    echo "Usage: $(basename "$0") [--verbose] [--debug] FILE"
}

set +e
commandline=$(getopt --name "$(basename "$0"): Error" \
    --options='?hvd' --longoptions='help,usage,verbose,debug' -- "$@")
if [ $? -ne 0 ]; then
    usage
    exit 2
fi
set -e
eval set -- "${commandline}"
verbose=0
debug=0
while true; do
    case "$1" in
        -\?|-h|--help|--usage)
            usage
            exit
            ;;
        -v|--verbose)
            verbose=1
            ;;
        -d|--debug)
            debug=1
            ;;
        --)
            shift
            break
            ;;
        *)
            error "Invalid getopt(1) output."
            exit 1
            ;;
    esac
    shift
done
if [ $# -ne 1 ]; then
    usage
    exit 2
fi

file=$(readlink --canonicalize-missing "$1")

if [ "${verbose}" -eq 1 -o "${debug}" -eq 1 ]; then
    enable_verbose
fi
if [ "${debug}" -eq 1 ]; then
    enable_debug
fi

packages=$(deb_file_packages "${file}")

if [ -n "${packages}" ]; then
    debug "File '${file}' found in the following packages: " \
	"$(printf '\n%s\n' "${packages}")"
    for package in ${packages}; do
	md5sums=$(deb_conffiles_md5sums "${package}" | file_md5sum "${file}")

	if [ -n "${md5sums}" ]; then
	    check_md5sums "${package}" "${file}" "${md5sums}"
	else
	    debug "No MD5 sum found in package '${package}'."
	fi
    done
else
    verbose "No package containing '${file}' found."
fi

if [ -d "${MD5DIR}" ]; then
    debug "Checking extra MD5 sums..."
    for md5file in $(find "${MD5DIR}" \
	-name '*~' -prune -o -name '*.*' -prune -o -type f -print); do

	package=$(basename "${md5file}")
	md5sums=$(reformat_md5sums "${md5file}" | file_md5sum "${file}")

	check_md5sums "${package}" "${file}" "${md5sums}"
    done
else
    warn "Not checking extra MD5 sums of non-conffiles: Can not access '"${MD5DIR}"'."
fi

verbose "'${file}' does not have the same MD5 sum as distributed by any" \
    "package."
exit 1
