#!/bin/sh -efu
#
# The ALT checksum repository tool.
#
# Copyright (C) 2024  Paul Wolneykien.
#
# 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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA

PROG="${0##*/}"
VERSION="0.1.4"
YEAR="2024"

REMOTE="$PROG"
PWD="${PWD:-$(pwd)}"

CMD="$0"

has_git() {
    [ -d "${GIT_DIR:-.git}" ]
}

has_remote() {
    has_git || return 1
    git remote | grep -q "^$REMOTE\$"
}

verb() {
    (
	set -x
	"$@"
    )
}

find_gpg() {
    if ! GPGCMD="$(git config gpg.program)"; then
	if ! GPGCMD="$(which gpg)"; then
	    if ! GPGCMD="$(which gpg2)"; then
		echo "ERROR: Please, install GnuPG (ver. 1 or 2) or configure the installed version with \`git config gpg.program\`." >&2
		return 1
	    fi
	fi
    fi
}

with_keys() {
    (
	trap '[ -z "${workdir:-}" ] || rm -rf "$workdir"' EXIT
	workdir="$(mktemp -d --tmpdir "$PROG.XXXX")"
	export GNUPGHOME="$workdir"
	cat <<EOF >"$workdir"/gpg.conf
no-greeting
lock-never
always-trust
no-secmem-warning
quiet
EOF
	find_gpg
	"$CMD" keys | "$GPGCMD" --import
	"$@"
    )
}

case "${1:-}" in
    version)
	cat <<EOF
$PROG $VERSION $YEAR
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.
EOF
	exit 0
	;;
    init)
	! has_git || exit 0
	verb git init ${2:+"$2"}
	;;
    url)
	case "${2:-}" in
	    '')
		if has_git && has_remote; then
		    git remote get-url "$REMOTE"
		else
		    "$CMD" url \? | head -1 | cut -f2 -d' '
		fi
		;;
	    \?)
		cat <<EOF
1) https://checksum.altsp.su/alt-checksum/checksums.git
2) https://gitlab.basealt.space/alt-checksum/checksums.git
EOF
		;;
	    [0-9]*)
		(
		    url_n="$("$CMD" url \? | tail -n +"$2" | head -1 | cut -f2 -d' ')"
		    if [ -n "$url_n" ]; then
			"$CMD" url "$url_n"
		    else
			echo "No preconfigured URL number $2. Type '$PROG url \?' to display list of known URLs." >&2
			exit 1
		    fi
		)
		;;
	    *)
		has_git || "$CMD" init
		if has_remote; then
		    verb git remote set-url "$REMOTE" "$2"
		    verb git remote prune "$REMOTE"
		else
		    verb git remote add "$REMOTE" "$2"
		fi
		;;
	esac
	;;
    add)
	case "${2:-}" in
	    \?)
		git ls-remote -q --refs --heads "$("$CMD" url)" | \
		    while read -r _ path; do
			echo "${path#refs/heads/}"
		    done
		;;
	    '')
		"$CMD" help add >&2
		exit 1
		;;
	    *)
		has_remote || "$CMD" url "$("$CMD" url)"
		verb git fetch -pP "$REMOTE" refs/heads/"$2":"$2"
		git branch -u "$REMOTE"/"$2" "$2"
		verb mkdir -p "$2"
		verb git worktree add "$2" "$2"
		verb "$CMD" validate "$2"
		;;
	esac
	;;
    show)
	has_remote || exit 0
	case "${2:-}" in
	    \?)
		git branch --list --format='%(upstream:remotename) %(refname:short) %(worktreepath)' | grep "^$REMOTE " | \
		    while read -r _ name path; do
			case "$path" in
			    "$PWD"/*)
				echo "$name"
				;;
			esac
		    done
		;;
	    '')
		"$CMD" help show >&2
		exit 1
		;;
	    *)
		git log --format=full -1 refs/heads/"$2" | cat
		;;
	esac
	;;
    log)
	has_remote || exit 0
	case "${2:-}" in
	    \?)
		"$CMD" show \?
		;;
	    '')
		"$CMD" help log >&2
		exit 1
		;;
	    *)
		git log refs/heads/"$2"
		;;
	esac
	;;
    del)
	has_remote || exit 0
	case "${2:-}" in
	    \?)
		"$CMD" show \?
		;;
	    '')
		"$CMD" help del >&2
		exit 1
		;;
	    *)
		verb git worktree remove --force "$2"
		verb git branch -D "$2"
		verb git gc
		;;
	esac
	;;
    update)
	has_remote || exit 0
	case "${2:-}" in
	    \?)
		"$CMD" show \?
		;;
	    '')
		"$CMD" update \? | while read -r br; do
		    "$CMD" update "$br"
		done
		;;
	    *)
		(
		    set -x
		    cd "$2"
		    git pull --ff-only "$REMOTE"
		)
		verb "$CMD" validate "$2"
		;;
	esac
	;;	
    keys)
	case "${2:-}" in
	    \?)
		find_gpg
		with_keys verb "$GPGCMD" --list-keys
		;;
	    '')
		cat <<EOF
-----BEGIN PGP PUBLIC KEY BLOCK-----

mQINBGB+6jsBEADCocs0El14Mfbdgzd70dcO4W2VvGAjJSRahejs6A8CivUjloak
aQYGgOPN8iUWEj+wrVttRd/9Ji9Su+WrIfXBo8chH9mMVzDR7M/L1btyreVhvHh3
29LGVi0N5kdwYKLawM6AJq+i2ZwDQR7lu45gQa2ptrPei2Kc3p67jOkX2NRRye3/
MZBgPTCY3E7gYzFXzDPB0hFufk/fZRyIj0Vg1SSZuQOOotVL0D3PKxB54L76rvSb
YZFf9Ju+o8gj7vt88RX7CWdVVnOicbR7jV534tjO16mqqf/vMVGR5E1IiAZo4i4V
iSuCe6LCYiaFt/xydVX/yhNeX0qMXlONiNv5yYcKbddvNtaQPbpxgjy7PopV9gBS
W9/wLYQn/S9yqu76nl4mS7a5rVfYsSfkFCHJeXx8Zpsx6+zGUvV9EV943Gmmr8rc
LOZrI/V2Rty7+FZRPdmYy3b/mX+XkUTI3xc8ip/BmHkQMCytr9DjdoCdQjUtoZBq
ZOLASZugnI7CEkn10TaPhH15y4Gi6dj3ukmPfjPr5uDrTyA0CeoWwVC/15F5cmrZ
cB9P1MCnWfnJpsKm6KtJRDhKBxSXXEh7WuvgfIo+kHMgGxCEAY7FVU4MQ874iLto
J0rfMJGy1o3qRcGmFrLrgGs8tBYcXX9+JYL6+BZHjz9Qoz+xrwnCeL42LwARAQAB
tCZQYXVsIFdvbG5leWtpZW4gPG1hbm93YXJAYWx0bGludXgub3JnPokCVAQTAQgA
PgIbAwULCQgHAgYVCgkICwIEFgIDAQIeAQIXgBYhBF/IjB0W4/VMwxo1jG4r/ce5
8ihkBQJmKrvIBQkHjQUNAAoJEG4r/ce58ihkQ8oQAKUCPdVK/Bv/9g1dJEyJYF+3
ThKWV+txGa6rm6GsQ94skxjK/8xZGdzafgUfyX5XNjgrpgCgEyMjXL+A2unO0drB
UrInpUumemIlSgnMyGARfHIL87GKvwtDw4/Hb1GeJBJpZ2FwCHnq71QQ3h+NAilF
zeZNMj2/AOv+6ZFFlMi+8uMPLrF2hM+sVFb/0+lcg76bWkmyJqDx6JVHGsymYcbW
6BnXczjHJ6t4cnpQa0JxIvWe+MM2ebfCHGX6fsmB2UvW6ekdVcJTKUFKQVDBLfq2
bze0ehP5t3Qy3ponfZrHWw2CIFjCOnMtvO4xnzcep2mqbcfiOQPPATJ9CEt1KQ+G
M1UnrTvCyFqyVqWYYDGiwGFXI84SRG758Wcn8b4SyqpUH67lX1VSH45VUTbl8aeh
jIeQx9N+dFJEn4G7Epe/C8xjkNW356MgVjqGLb66Jd7HTLTNrsqEP4+E3QsoGymf
KqOy5WFr4wbni6A3T3mRjo3thh9DT1jmoAlrs3JUHpUZ+qysclOagYO88hlGkhl0
yz1R4zW6ddUxtpZPhTcZgKjAOOq8D1GidTZthcTy4vZ5kiapgzFf387OvhsNktbR
HkIlztCJb1Shjrdqcqha9JivBqpQHuqWL4338VLg2zL2Ohrbb2nhhqYmYHUxd5eo
KGUYwyL7Cw3t+cSLYNSf
=hjX2
-----END PGP PUBLIC KEY BLOCK-----
-----BEGIN PGP PUBLIC KEY BLOCK-----

mQINBGbSIIoBEAC/tCq5ZnwBpF7wly+yi9CUc7KJ9RevQK88Tll/eojDB8EzSJNm
Ie+MH5VRtAWI63YbhypPi1+fyF813UdW5JJDrjatQ02eKMwZnK0oOelvtAw+Zr4y
6YjKdPrvMA89KMk6sZMqhIVexuqhWQAXiGNx7Z3g52E33BxY0eHPyEdCWORcsrrm
Nive/5qjTMZ103a2Dvc2yztZob/b5EnjOEi8tJc2jB5cwc/biIAX6ZG+2oTUAydB
3EgNXefWOM5U4CjSbH+HSEf3h1eDl6Ehr+YlaKwylb+gnYkVS/oOfLTbgp7J1M+c
1aTIJ+av9YdSgkv3Hl4NhHxyAhHKXGplbgVQO/yzB7CY3Fi1OIn99afKkeGmc8tL
tw5JgXrTsnKZ2kC77GRAcRhRV8Y7IX4F/t/eZASwA183o74WNi0m46V8Y2sCJSTo
L8f4rLm7yxLNQOl0l1hntDeLt4BfP77d2igCX9VJiq+hg1IXdvUkHoVCtPpNN7yH
EmhAtjtu8MbP6sAKE1DXdYwxIcnabkma7K6pSqKbn9XowZ6vmtPJollNaZmS3MHW
9ZZoaibLq90uB9xDMT/Q5w4usrwPxtXunjgSqTJIt4rccyguJREVpLHxkqwgp7LP
bzuBnEqEqgwpUCr08JRwBk4qnSfp4C9LZniGzMTirKd8a+GO5iFhzNexxQARAQAB
tCZDaGVja3N1bSBUZXN0IDxjaGVja3N1bXRlc3RAbG9jYWxob3N0PokCPgQTAQgA
KAUCZtIgigIbAwUJAeEzgAYLCQgHAwIGFQgCCQoLBBYCAwECHgECF4AACgkQtOps
4xY1F8b6jQ//U/RQNeWXu3ign2HVn1Jk1xzUY+ke10gNXd2YQW8ESh10ShQbtJcK
yE+DMVLvNnCZPXVt+Ct5zruc+ZMhI4QKpwYnqDXTafSNWFVjrNdR9vv1J0HBuiAn
N6ZsbCShIQ+60MfZ94vQ3AwNBPyugTn/9MmpoEfbyMJEObv8oKA1EUpm00se+ODn
lk3bsFbIbEzKGNgY/nkZLzupJU9sy2groL3rfcrdG3C/kaWd2azuGlX786ym/CE9
eQfGlSIlmzg8m8Op4P/cEspILM+F3zklIRJfa4yeq/X4zt79y+NcxQ5OOLbhr/pW
NG3ji+qk2OsjhTx1GR2y/SrbLaEwJtE8vKrayFQPShqwue0rlWHQVGmuQlFt3GJ8
VMDFzgEwnVpThHPl3ntYPut/FFxurdiAdf04wr5GearOndmbSsNMRiA5kGCmP45h
M0rBQn8m5sC3ejjKitqpQeU7oUUfJrrXZrueOGKurHF0PjilwJoG70KBhYodQWNW
gHTvXClDNFHl/e58EgO4bw0mJmcyvxDB0Qeeyvk+URhIgRK8RqA9HUYfwxcYjH4E
IjLEWj+UrYEKinSreYPkTHfiWJl9GWVRpMr782ueyN3RvlZC1dkxNHEYdKdQacaI
K6TflCaDfazJBSdSXqhbonEy3d8KcORv2MHsc5U+pU2rl2PGPap+XnQ=
=AThg
-----END PGP PUBLIC KEY BLOCK-----
EOF
		;;
	    *)
		"$CMD" help keys >&2
		exit 1
		;;
	esac
	;;
    validate)
	case "${2:-}" in
	    \?)
		"$CMD" show \?
		;;
	    '')
		if ! has_remote; then
		    echo 'ERROR: Uninitialized checksum repository.' >&2
		    exit 1
		fi

		if [ -z "$("$CMD" validate \?)" ]; then
		    echo "ERROR: Nothing to validate! Add some branches with \`$PROG add\`" >&2
		    exit 1
		fi

		"$CMD" validate \? | while read -r br; do
		    "$CMD" validate "$br" || exit $?
		done
		;;
	    *)
		(
		    cd "$2"
		    if [ -n "$(verb git status -unormal --porcelain)" ]
		    then
			git status -unormal
			echo "ERROR: Untracked files and/or not-committed changes found in $2." >&2
			exit 1
		    fi

		    tag="$(git tag --points-at refs/heads/"$2")"

		    case "$(git log -1 --format='%G?' HEAD)" in
			N)
			    if [ -z "$tag" ]; then
				echo "ERROR: No signed tag found for the unsigned HEAD commit in $2." >&2
				exit 1
			    fi
			    with_keys verb git tag -v "$tag"
			    ;;
			*)
			    with_keys verb git verify-commit -v HEAD
			    [ -z "$tag" ] || with_keys verb git tag -v "$tag"
			    ;;
		    esac
		)
		;;
	esac
	;;
    verify)
	shift
	(
	    dirs="$("$0" show \?)"
	    if [ -z "$dirs" ]; then
		echo "ERROR: No branches yet! Add some branches with \`$PROG add\`" >&2
		exit 1
	    fi
	    #shellcheck disable=SC2046
	    verb verify-checksums $(for d in $dirs; do echo --dir "$d"; done) "$@"
	)
	;;
    help)
	case "${2:-}" in
	    url)
		cat <<EOF
Usage: $PROG url [\? | <n> | https://...]
\?       -- display preconfigured enumerated set of URLs;
<n>      -- select an URL with the given number;
https:// -- select the given (custom) URL.
EOF
		;;
	    add)
		cat <<EOF
Usage: $PROG add \? | <branch>
\?       -- list branches available for the selected URL;
<branch> -- download and checkout a branch with the given name.
EOF
		;;
	    show)
		cat <<EOF
Usage: $PROG show \? | <branch>
\?       -- list the currently checked-out branches;
<branch> -- display detailed information about the branch.
EOF
		;;
	    log)
		cat <<EOF
Usage: $PROG log \? | <branch>
\?       -- list the currently checked-out branches;
<branch> -- display log of the given branch.
EOF
		;;
	    del)
		cat <<EOF
Usage: $PROG del \? | <branch>
\?       -- list the currently checked-out branches;
<branch> -- remove the local copy of the branch.
EOF
		;;
	    update)
		cat <<EOF
Usage: $PROG update [\? | <branch>]
\?       -- list the currently checked-out branches;
<branch> -- download updates for the given branch;
         -- or for all currently checked-out branches.
EOF
		;;
	    validate)
		cat <<EOF
Usage: $PROG validate [ \? | <branch>]
\?       -- list the currently checked-out branches;
<branch> -- validate the signature of the given branch;
         -- or for all currently checked-out branches.
EOF
		;;
	    verify)
		cat <<EOF
Usage: $PROG verify [...options to verification script]
... -- all options are passed to the verify-checksum(1) script.
EOF
		;;
	    keys)
		cat <<EOF
Usage: $PROG keys [\?]
\? -- display metadata of available public keys;
   -- or dump the key data as is.
EOF
		;;
	    init)
		cat <<EOF
Usage: $PROG init [path/to/dir...]
path/to/dir... -- initialize the given directory for checksum data;
               -- otherwise, initialize the current directory.
EOF
		;;
	    help)
		cat <<EOF
Usage: $PROG help [<command>]
<command> -- display usage information on the given command;
          -- or the general usage information.
EOF
		;;
	    version)
		cat <<EOF
Usage: $PROG version
display $PROG version and license information and exit.
EOF
		;;
	    *)
		cat <<EOF
Usage: $PROG <command> ...
$PROG url [\? | <n> | https://...]
$PROG add \? | <branch>
$PROG show \? | <branch>
$PROG log \? | <branch>
$PROG del \? | <branch>
$PROG update [\? | <branch>]
$PROG validate [ \? | <branch>]
$PROG verify [...options to verification script]
$PROG keys [\?]
$PROG init [path/to/dir...]
$PROG help [<command>]
$PROG version
EOF
		;;
	esac
	;;
    *)
	"$CMD" help >&2
	exit 1
	;;
esac
