#!/bin/sh
#
# pkgsums
# 2015-04-23
# by Gernot Walzl
#
# Check files of installed packages using checksums.
# These checksums need to be generated before.

PKGDIR="/var/log/packages"
CHECKSUMDIR="/var/log/checksums"
CHECKSUMEXT="sha1"
CHECKSUMCMD="sha1sum"

print_usage () {
  echo "Usage: $0 {generate|check} [PACKAGE]"
}

get_full_pkg_name () {
  PKG="$1"
  if [ ! -f "${PKGDIR}/${PKG}" ]; then
    PKG_FULL=$(cd $PKGDIR; ls -rv ${PKG}-* 2> /dev/null \
      | grep -e "^${PKG}-[^-]\+-[^-]\+-[^-]\+\$" -m 1)
    if [ ! -z "$PKG_FULL" ]; then
      PKG="$PKG_FULL"
    fi
  fi
  echo "$PKG"
}

checksum_pkg () {
  PKG="$1"
  if [ ! -f "${PKGDIR}/${PKG}" ]; then
    echo "ERROR: Package $PKG not found."
    return 1
  fi
  echo "$PKG"
  if [ ! -d "$CHECKSUMDIR" ]; then
    mkdir -p "$CHECKSUMDIR"
  fi
  if [ -f "${CHECKSUMDIR}/${PKG}.${CHECKSUMEXT}" ]; then
    rm -f "${CHECKSUMDIR}/${PKG}.${CHECKSUMEXT}"
  fi
  cd /
  FILES=$(sed '1,/FILE LIST:/d' "${PKGDIR}/${PKG}" \
    | grep -v '^install/' \
    | grep -v '\.new$' | sort)
  IFS=$'\n'
  for FILE in $FILES; do
    FILE=$(printf '%b' "$FILE")   # decode \xxx chars
    if [ -f "$FILE" ]; then
      $CHECKSUMCMD "$FILE" >> "${CHECKSUMDIR}/${PKG}.${CHECKSUMEXT}"
    elif [ ! -d "$FILE" ]; then
      echo "WARNING: $FILE"
    fi
  done
}

generate () {
  for PKG in $(ls $PKGDIR); do
    if [ ! -f "${CHECKSUMDIR}/${PKG}.${CHECKSUMEXT}" ]; then
      checksum_pkg "$PKG"
    fi
  done
}

check_pkg () {
  PKG="$1"
  if [ ! -f "${PKGDIR}/${PKG}" ]; then
    echo "ERROR: Package $PKG not found."
    return 1
  fi
  if [ ! -f "${CHECKSUMDIR}/${PKG}.${CHECKSUMEXT}" ]; then
    echo "ERROR: Package $PKG has no checksums."
    return 1
  fi
  echo "$PKG"
  cd /
  grep -v '\.new$' "${CHECKSUMDIR}/${PKG}.${CHECKSUMEXT}" \
    | grep -v 'usr/share/fonts/.*/fonts\.dir$' \
    | grep -v 'usr/share/fonts/.*/fonts\.scale$' \
    | $CHECKSUMCMD -c - --quiet
  return $?
}

check () {
  NUM_SUCCESS=0
  NUM_FAILED=0
  NUM_MISSING=0
  FAILED_PKGS=""
  for PKG in $(ls $PKGDIR); do
    if [ -f "${CHECKSUMDIR}/${PKG}.${CHECKSUMEXT}" ]; then
      check_pkg "$PKG"
      if [ $? -eq 0 ]; then
        NUM_SUCCESS=$(($NUM_SUCCESS+1))
      else
        NUM_FAILED=$(($NUM_FAILED+1))
        FAILED_PKGS="$FAILED_PKGS
$PKG"
      fi
    else
      echo "WARNING: Package $PKG has no checksums."
      NUM_MISSING=$(($NUM_MISSING+1))
    fi
  done
  echo ""
  echo "---- SUMMARY ----"
  echo "Success: $NUM_SUCCESS"
  echo "Failed: $NUM_FAILED"
  echo "Missing: $NUM_MISSING"
  echo ""
  if [ ! -z "$FAILED_PKGS" ]; then
    echo "Failed Packages: $FAILED_PKGS"
    echo ""
    return 1
  fi
}

clean () {
  for CHECKSUM in $(ls $CHECKSUMDIR); do
    PKG=$(basename $CHECKSUM ".${CHECKSUMEXT}")
    if [ ! -f "$PKGDIR/$PKG" ]; then
      rm -fv "$CHECKSUMDIR/$CHECKSUM"
    fi
  done
}

case "$1" in
  'generate')
    if [ "$2" = "" ]; then
      generate
    else
      checksum_pkg "$(get_full_pkg_name $2)"
    fi
    ;;
  'check')
    if [ "$2" = "" ]; then
      check
    else
      check_pkg "$(get_full_pkg_name $2)"
    fi
    ;;
  'clean')
    clean
    ;;
  *)
    print_usage
esac
