#!/bin/sh
set -eu

database=/var/db/mouse/packages

fail()
{
	printf '%s\n' "mouse-package: $*" >&2
	exit 1
}

safe_path()
{
	case "$1" in
		""|/*|.|..|../*|*/../*|*/..|./*|*/./*) return 1 ;;
		*) return 0 ;;
	esac
}

manifest_value()
{
	sed -n "s/^$1=//p" "$2/+MANIFEST"
}

add_package()
{
	package=$1
	[ -f "$package/+MANIFEST" ] || fail "missing package manifest"
	[ -f "$package/+PLIST" ] || fail "missing packing list"
	[ -d "$package/root/usr/local" ] || fail "payload escapes /usr/local"
	name=$(manifest_value name "$package")
	version=$(manifest_value version "$package")
	case "$name:$version" in
		*[!A-Za-z0-9._+:-]*) fail "invalid package identity" ;;
	esac
	mkdir -p "$database"
	[ ! -e "$database/$name" ] || fail "$name is already installed"
	while IFS= read -r path; do
		safe_path "$path" || fail "unsafe packing-list path: $path"
		for record in "$database"/*/+PLIST; do
			[ -f "$record" ] || continue
			grep -Fqx "$path" "$record" && fail "$path is already owned"
		done
	done <"$package/+PLIST"
	cp -R "$package/root/usr/local/." /usr/local/
	temporary=$database/.$name.new
	rm -rf "$temporary"
	mkdir -p "$temporary"
	cp "$package/+MANIFEST" "$temporary/+MANIFEST"
	cp "$package/+PLIST" "$temporary/+PLIST"
	printf '%s\n' "$version" >"$temporary/version"
	mv "$temporary" "$database/$name"
}

delete_package()
{
	name=$1
	record=$database/$name
	[ -f "$record/+PLIST" ] || fail "$name is not installed"
	sort -r "$record/+PLIST" |
	while IFS= read -r path; do
		safe_path "$path" || fail "unsafe installed path: $path"
		if [ -d "/usr/local/$path" ] && [ ! -L "/usr/local/$path" ]; then
			rmdir "/usr/local/$path" 2>/dev/null || true
		else
			rm -f "/usr/local/$path"
		fi
	done
	rm -rf "$record"
}

case "${1:-}" in
	add) [ "$#" -eq 2 ] || fail "usage: mouse-package add PACKAGE"; add_package "$2" ;;
	delete) [ "$#" -eq 2 ] || fail "usage: mouse-package delete NAME"; delete_package "$2" ;;
	status) [ "$#" -eq 2 ] || fail "usage: mouse-package status NAME"; test -f "$database/$2/+MANIFEST" ;;
	list)
		[ "$#" -eq 1 ] || fail "usage: mouse-package list"
		for record in "$database"/*; do
			[ -f "$record/+MANIFEST" ] || continue
			printf '%s-%s\n' "${record##*/}" "$(cat "$record/version")"
		done
		;;
	*) fail "usage: mouse-package {add|delete|status|list}" ;;
esac
