#!/usr/bin/env bash
#
# Description: Checks installed versions of programs for which there is
#              an apparmor profile for.
#
# Homepage: https://github.com/krathalan/apparmor-profiles
#
# Copyright (C) 2019-2024 krathalan
#
# 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 3 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, see <https://www.gnu.org/licenses/>.

# -----------------------------------------
# -------------- Guidelines ---------------
# -----------------------------------------

# This script follows the Google Shell Style Guide:
# https://google.github.io/styleguide/shell.xml

# This script uses shellcheck: https://www.shellcheck.net/

# This script creates a temporary folder and may fail halfway through
trap clean_up EXIT

# See https://vaneyckt.io/posts/safer_bash_scripts_with_set_euxo_pipefail/
set -Eeuo pipefail

# -----------------------------------------
# ----------- Program variables -----------
# -----------------------------------------

# Colors
GREEN=$(tput setaf 10)
RED=$(tput bold && tput setaf 1)
NC=$(tput sgr0) # No color/turn off all tput attributes

# For faster processing
SCRIPT_NAME=$(basename "$0")
TMPDIR="$(mktemp -d -t "${SCRIPT_NAME}_XXXXXXXX")"
readonly OUTPUTFILEBAD="${TMPDIR}/version-check-bad.txt"
readonly OUTPUTFILEGOOD="${TMPDIR}/version-check-good.txt"
touch "${OUTPUTFILEBAD}" "${OUTPUTFILEGOOD}"

readonly SCRIPT_NAME TMPDIR GREEN RED NC

# -----------------------------------------
# ------------- User variables ------------
# -----------------------------------------

quiet="false"

# -----------------------------------------
# --------------- Functions ---------------
# -----------------------------------------

#######################################
# Checks the version of the AppArmor profile against the installed package
# version.
# Globals:
#   Colors: GREEN, RED, NC
#   OUTPUTFILEBAD
#   OUTPUTFILEGOOD
# Arguments:
#   $1: AppArmor profile to check
# Returns:
#   none
#######################################
check_profile()
{
  local profile="$1"

  local profiledVersion
  profiledVersion="$(grep -E "Version of.*profiled" "/etc/apparmor.d/${profile}")"
  profiledVersion="${profiledVersion##* }"

  # Fix some profile names being different than their package name
  case "${profile}" in
    bluetoothd)
      profile="bluez" ;;
    gpg-agent)
      profile="gnupg" ;;
    mbsync)
      profile="isync" ;;
    NetworkManager)
      profile="networkmanager" ;;
    rngd)
      profile="rng-tools" ;;
    ssh|ssh-agent)
      profile="openssh" ;;
    systemd*)
      profile="systemd" ;;
    wl-copy)
      profile="wl-clipboard" ;;
  esac

  # Check firefox-developer-edition if firefox is not installed
  if [[ "${profile}" == "firefox" ]] && [[ -z "$(command -v "firefox")" ]]; then
    profile="firefox-developer-edition"
  fi

  local installedVersion
  installedVersion="$(pacman -Qi "${profile}" 2> /dev/null | grep Version | cut -d' ' -f11 || printf "not")"

  # Remove package release suffix from installed version
  installedVersion="${installedVersion%%-*}"
  installedVersion="${installedVersion%%p*}"
  installedVersion="${installedVersion%%+*}"
  installedVersion="${installedVersion%%.r*}"

  if [[ "${installedVersion}" == "not" ]]; then
    printf "%s %s installed\n" "${profile}" "${installedVersion}" >> "${OUTPUTFILEGOOD}"
  elif [[ "${profiledVersion}" == "git" ]]; then
    printf "%s installed, %s version profiled (unknown)\n" "${profile}" "${profiledVersion}" >> "${OUTPUTFILEGOOD}"
  elif [[ ! "${installedVersion}" == "${profiledVersion}" ]]; then
    printf "%s%s %s installed; %s profiled%s\n" "${RED}" "${profile}" "${installedVersion}" "${profiledVersion}" "${NC}" >> "${OUTPUTFILEBAD}"
  else
    printf "%s%s profile up to date%s\n" "${GREEN}" "${profile}" "${NC}" >> "${OUTPUTFILEGOOD}"
  fi
}

#######################################
# Removes temporary folder before exit.
# Globals:
#   TMPDIR
# Arguments:
#   none
# Returns:
#   none
#######################################
clean_up()
{
  if [[ -d "${TMPDIR}" ]]; then
    rm -rf "${TMPDIR}"
  fi
}

# -----------------------------------------
# ---------------- Script -----------------
# -----------------------------------------

# Print intro
if [[ "$(whoami)" = "root" ]]; then
  printf "%sThis script should NOT be run as root (or sudo)!%s\n" "${RED}" "${NC}" >&2
  exit 1
fi

if [[ $# -gt 0 ]]; then
  if [[ "$1" == "--quiet" ]] || [[ "$1" == "-q" ]]; then
    quiet="true"
  elif [[ "$1" == "-h" ]] || [[ "$1" == "--help" ]] || [[ "$1" == "help" ]]; then
    printf "%s" "\
kapvc - Krathalan's Apparmor Profile Version Checker

Compares the recorded supported program versions of
all of Krathalan's Apparmor profiles in /etc/apparmor.d/
to the installed progam versions.

Run with \`-q\` or \`--quiet\` to only show profiles
for which their supported program version does not
match the installed program version.
"
    exit
  fi
fi

# Get list of my profiles :)
mapfile -t list_of_profiles <<< "$(pacman -Qlq krathalans-apparmor-profiles-git | grep -vE 'usr|abstr|/$')"

for profile in "${list_of_profiles[@]}"; do
  check_profile "${profile##*/}" &
done

wait

if [[ "${quiet}" == "true" ]]; then
  sort "${OUTPUTFILEBAD}"
else
  sort "${OUTPUTFILEBAD}" "${OUTPUTFILEGOOD}"
fi
