#!/bin/sh
#
# Copyright (c) 2013 M:tier Ltd.
#
# Permission to use, copy, modify, and distribute this software for any
# purpose with or without fee is hereby granted, provided that the above
# copyright notice and this permission notice appear in all copies.
#
# THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
# OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
#
# Author: Antoine Jacoutot <antoine@mtier.org>

# TODO
# https://stable.mtier.org/vuxml/53.xml

### user defined variables
OPENUP_URL='https://stable.mtier.org/openup'
PKG_CERT_URL='https://stable.mtier.org/mtier.cert'
PKG_CERT_FPRINT='DE:29:0F:7F:B8:0E:36:5A:AF:A9:BF:E0:4E:08:C2:0F:2D:50:16:97'
### end of user defined variables

### DO NOT MODIFY ANYTHING BELOW THIS LINE!!!

usage() {
	echo
	echo "usage: ${0##*/} [-SUn]" >&2
	echo "         -S  do not check for package signatures"
	echo "         -U  do not check for openup update"
	echo "         -n  dry run mode, no modification is done"
	exit 1
}

pr_error() {
	echo "!!! ${@}"
}

if [ "$(id -u)" -ne 0 ]; then
	echo "need root privileges to run this script"
	usage
fi

# regex taken from fw_update(1)
set -A REL -- $(sysctl -n kern.version | sed 's/^OpenBSD \([0-9]\.[0-9]\)\([^ ]*\).*/\1 \2/;q')
REL_INT="$(echo ${REL[0]} | tr -d '.')"

if [ "${REL[1]}" == -!(stable) -o "${REL_INT}" -lt "${OPENUP_MINREL}" ]; then
	pr_error "${REL[0]}${REL[1]} is not a supported release"
	exit 1
fi

if ! grep -q installpath /etc/pkg.conf 2>/dev/null; then
	if [ -z "${PKG_PATH}" ]; then
		pr_error "No package repository defined in pkg.conf(5) nor PKG_PATH"
		pr_error
		pr_error "The \${PKG_PATH} environment variable can be set as follow:"
		pr_error "PKG_PATH=http://ftp.openbsd.org/pub/OpenBSD/\$(uname -r)/packages/\$(arch -s)"
		pr_error "PKG_PATH=https://stable.mtier.org/updates/\$(uname -r)/\$(arch -s):\${PKG_PATH}"
		pr_error "export PKG_PATH"
		exit 1
	fi
fi

# check that we have no installed binpatches from a previous release
_BPLIST=$(pkg_info -q | grep "^binpatch")
if [ -n "${_BPLIST}" ]; then
	_i=$(echo ${_BPLIST} | wc -w)
	_v=$(echo ${_BPLIST} | grep "^binpatch${REL_INT}" | wc -w)
	if [ "${_i}" -ne "${_v}" ]; then
		pr_error "Binpatch(es) from a previous release installed"
		exit 1
	fi
fi
unset _BPLIST

OPENUP_VERSION=2
OPENUP_MINREL=53
MEESA=$(readlink -f $0)
TMPDIR="${TMPDIR:=/tmp}"
TMPCERT=$(mktemp -p ${TMPDIR} openup-pkg_cert.XXXXXX) || exit 1
TMPUPD=$(mktemp -p ${TMPDIR} openup-update.XXXXXX) || exit 1
TMPSPLITCERT=$(mktemp -p ${TMPDIR} openup-split_cert.XXXXXX) || exit 1

trap "rm -f ${TMPDIR}/openup-*" 1 2 3 13 15

pr_bigarrow() {
	echo "===> ${@}${DRYRUN}"
}

pr_smallarrow() {
	echo "-> ${@}"
}

get_cert() {
	local _CMD

	[[ -r /etc/ssl/pkgca.pem ]] && \
		split -p '^-----BEGIN CERTIFICATE-----$' /etc/ssl/pkgca.pem ${TMPSPLITCERT}

	for i in ${TMPSPLITCERT}* ; do
		openssl x509 -noout -in $i -fingerprint 2>/dev/null | \
			grep -q "${PKG_CERT_FPRINT}" && return
	done

	pr_bigarrow "Downloading certificate"
	_CMD="ftp -Vo ${TMPCERT} ${PKG_CERT_URL}"
	pr_smallarrow "${_CMD}"
	if [ -z "${DRYRUN}" ]; then
		eval ${_CMD} || exit 1
	fi

	pr_bigarrow "Installing certificate"
	_CMD="cat ${TMPCERT} >>/etc/ssl/pkgca.pem"
	pr_smallarrow "${_CMD}"
	if [ -z "${DRYRUN}" ]; then
		eval ${_CMD} || exit 1
	fi
}

update_self() {
	local _CMD _N

	pr_bigarrow "Checking for openup update"
	_CMD="ftp -Vo ${TMPUPD} ${OPENUP_URL}"
	pr_smallarrow "${_CMD}"
	if [ -z "${DRYRUN}" ]; then
		eval ${_CMD} || exit 1
	fi

	_N=$(grep -Eo '^OPENUP_VERSION=.*' ${TMPUPD} | awk -F '=' '{ print $2 }')
	if [ -n "${_N}" -o -n "${DRYRUN}" ]; then
		if [ "${OPENUP_VERSION}" -lt "${_N}" -o -n "${DRYRUN}" ]; then
			pr_bigarrow "Updating openup to the latest release"
			_CMD="cat ${TMPUPD} >${MEESA}"
			pr_smallarrow "${_CMD}"
			if [ -z "${DRYRUN}" ]; then
				eval ${_CMD} || exit 1
				rtn=$?
			fi
			pr_bigarrow "openup has been updated, restarting"
			_CMD="${MEESA} -U $@"
			pr_smallarrow "${_CMD}"
			if [ -z "${DRYRUN}" ]; then
				eval ${_CMD}
				exit $rtn
			fi
		fi
	fi
}

update_binpatches() {
	local _BP _CMD

	_BP=$(pkg_info -Q binpatch${REL_INT}-$(arch -s) | sed 's/.[^-]*$//' | sort -u | tr '\n' ' ')

	if [ -n "$_BP" ]; then
		pr_bigarrow "Updating binpatch(es)"
		_CMD="pkg_add ${pkg_opt} ${_BP}"
		pr_smallarrow "${_CMD}"
		eval ${_CMD}
	fi
}

update_pkg() {
	local _CMD

	pr_bigarrow "Updating package(s)"
	_CMD="pkg_add -u ${pkg_opt}"
	pr_smallarrow "${_CMD}"
	eval ${_CMD}
}

update_fw() {
	local _CMD

	pr_bigarrow "Updating firmware(s)"
	_CMD="fw_update ${fw_opt}"
	pr_smallarrow "${_CMD}"
	eval ${_CMD}
}

while getopts 'SUn' arg; do
	case ${arg} in
	S)	NOSIG=1; pkg_opt="${pkg_opt} -D nosig" ;;
	U)	NOSELFUPDATE=1 ;;
	n)	DRYRUN=" (dry run)"; pkg_opt="${pkg_opt} -n -D nosig"; fw_opt="-n" ;;
	*)	usage ;;
	esac
done
[ $# = $(($OPTIND-1)) ] || usage

[[ -z $NOSELFUPDATE ]] && update_self
[[ -z $NOSIG ]] && get_cert
update_binpatches
update_pkg
update_fw

rm -f ${TMPDIR}/openup-*
