#!/usr/bin/env bash
#
# Description: Watches a specified directory for new packages and
#              moves them to a specified pacman repo.
#
# Homepage: https://codeberg.org/krathalan/krack
# Copyright (C) 2020-2024 Hunter Peavey
# SPDX-License-Identifier: GPL-3.0-or-later

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

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

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

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

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

# Source user conf
# shellcheck disable=SC1091
source /etc/krack/receive.conf

# Directory to watch for new packages
readonly DROPBOX_PATH="/home/krack-receive/package-dropbox"

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

add_packages()
{
  # Wait for the package to get here (rsync)...
  # We have to check to make sure that all files in the directory
  # end in .pkg.tar.zst or .pkg.tar.zst.sig (i.e., not currently in
  # transit via rsync, as it would end in e.g. ".xDSFghaA")
  local check_files=("${DROPBOX_PATH}"/*)
  local check_passed="false"

  while [[ "${check_passed}" == "false" ]]; do
    print_info "Scanning ${DROPBOX_PATH} for complete packages..."

    for file in "${check_files[@]}"; do
      if [[ "${file}" == *".pkg.tar.zst" ]] || [[ "${file}" == *".pkg.tar.zst.sig" ]]; then
        check_passed="true"
      else
        check_passed="false"
        print_info "Package files still loading. Napping..."
        break
      fi
    done

    # If packages still loading, take a lil nap :)
    sleep 7
    # Refresh data
    local check_files=("${DROPBOX_PATH}"/*)
  done

  print_info "Loading finished. Adding packages to repo ${REPO_ROOT}..."

  (
  cd "${REPO_ROOT}" || exit_script_on_failure "Failure to \`cd\` into ${REPO_ROOT}"

  for package in "${DROPBOX_PATH}"/*.pkg.tar.zst; do
    # Check for preexisting package with same version
    if [[ -f "${PWD}/${package##*/}" ]]; then
      print_info "Package with that version already exists. Removing package and signature from ${DROPBOX_PATH}..."

      # It's okay to not specify $DROPBOX_PATH here before the file names because
      # $package will contain a full non-relative path
      rm -f "${package}" "${package}.sig"
      continue
    fi
    
    # Check for signature if REJECT_UNSIGNED_PACKAGES=true
    if [[ "${REJECT_UNSIGNED_PACKAGES:-true}" == "true" ]]; then
      if [[ ! -f "${package}.sig" ]]; then
        print_error "Could not locate signature for package ${package}. Removing unsigned package file..."
        rm -f "${package}"
        continue
      fi
    fi

    # Package is valid, add to repo!
    mv "${package}" "${package}.sig" .
    repo-add -R -n "${REPO_DB_FILE}" "${package##"${DROPBOX_PATH}"/}"
  done
  )

  print_info "Done adding packages."
}

#######################################
# Prints passed error message before premature exit.
# Prints everything to >&2 (STDERR).
# Globals:
#   none
# Arguments:
#   $1: error message to print
# Returns:
#   none
#######################################
exit_script_on_failure()
{
  printf "ERROR: %s\n" "$1" >&2
  exit 1
}

#######################################
# Prints an info message.
# Globals:
#   none
# Arguments:
#   $1: info message to print
# Returns:
#   none
#######################################
print_info()
{
  printf "Info: %s\n" "$1"
}

#######################################
# Prints an error message.
# Globals:
#   none
# Arguments:
#   $1: error message to print
# Returns:
#   none
#######################################
print_error()
{
  printf "!! ERROR: %s\n" "$1"
}

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

# Test both directories to fail early if they are unwritable
readonly testFileName=".krack-receive-test-file"
toDie="false"

if ! touch "${DROPBOX_PATH}/${testFileName}" &> /dev/null; then
  print_error "Unable to write to ${DROPBOX_PATH}"
  toDie="true"
fi

if ! touch "${REPO_ROOT}/${testFileName}" &> /dev/null; then
  print_error "Unable to write to ${REPO_ROOT}"
  toDie="true"
fi

if [[ "${toDie}" == "true" ]]; then
  exit 1
fi

rm -f "${DROPBOX_PATH}/${testFileName}" "${REPO_ROOT}/${testFileName}"

print_info "Watching ${DROPBOX_PATH} for packages to add to ${REPO_ROOT}..."

while inotifywait -e modify "${DROPBOX_PATH}"; do
  add_packages
done
