#!/bin/sh
#
#    manifest - import/export a list of packages
#    Copyright (C) 2016 Dustin Kirkland
#
#    Authors: Dustin Kirkland <kirkland@byobu.org>
#
#    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, version 3 of the License.
#
#    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 <http://www.gnu.org/licenses/>.

set -e

if ! command -v pastebinit >/dev/null 2>&1; then
	echo "ERROR: pastebinit not found.  Hint:" 1>&2
	echo "  sudo apt install pastebinit" 1>&2
	exit 1
fi

export_packages() {
	if [ "$OBJECT" = "-" ]; then
		dpkg -l
	elif echo "$OBJECT" | grep -qs ".*://.*"; then
		dpkg -l | gzip -9 | base64 | pastebinit -b "${OBJECT##*/}"
	else
		dpkg -l > "$OBJECT"
	fi
}

# Only well-formed Debian package names reach apt.  Every token here comes
# from a manifest we did not write (a pastebin, a URL, a file someone sent),
# and it is passed to "sudo apt install": a token beginning with "-" would be
# parsed by apt as an option, and -oDPkg::Pre-Invoke::=... runs a shell as
# root.  Debian Policy 5.6.1: lowercase alphanumerics plus "+-.", at least two
# characters, starting with an alphanumeric.
filter_packages() {
	grep "^ii[[:space:]]" | col2 | grep -E '^[a-z0-9][a-z0-9.+-]+$' | sort -u
}

import_packages() {
	local pkgs
	case "$OBJECT" in
		-)
			pkgs=$(filter_packages)
		;;
		https://*)
			pkgs=$(wget -q -O- "$OBJECT" | grep -A 999999999 '<div class="paste"><pre>' | grep -B 999999999 "^</pre>" | sed -e "s/.*<pre>//" -e "s/^<\/pre>.*//" | base64 -d | gunzip | filter_packages)
		;;
		*://*)
			echo "ERROR: refusing to import a package list over an unencrypted URL; use https://" 1>&2
			exit 1
		;;
		*)
			pkgs=$(filter_packages < "$OBJECT")
		;;
	esac
	if [ -z "$pkgs" ]; then
		echo "ERROR: no valid package names found in $OBJECT" 1>&2
		exit 1
	fi
	echo "Packages to install:"
	echo "$pkgs" | tr '\n' ' ' | fold -s -w 78
	echo
	printf "Proceed with 'sudo apt install'? [y/N] "
	read -r ans < /dev/tty || exit 1
	case "$ans" in
		y|Y|yes|YES) ;;
		*) echo "Aborted." 1>&2; exit 1 ;;
	esac
	# shellcheck disable=SC2086  # $pkgs is validated, one name per word
	sudo apt install -- $pkgs
}

usage() {
	cat <<EOF
USAGE:
  $0 export [-|URL|FILENAME]
  $0 import -|URL|FILENAME
EOF
	exit 0
}

if [ -n "$1" ]; then
	ACTION="$1"
	shift
else
	usage
fi
if [ -n "$1" ]; then
	OBJECT="$1"
	shift
fi
if [ "$ACTION" = "import" ]; then
	import_packages
elif [ "$ACTION" = "export" ]; then
	[ -n "$OBJECT" ] || OBJECT="https://paste.ubuntu.com"
	export_packages
else
	usage
fi
