#! /usr/bin/env bash
url="$1"
file="$2"
hash="$3"
if [ "${#}" != '3' ]; then
	echo 'Usage: download <url> <file> <hash>' >&2
	exit 1
fi
for tryDownloadProgram in wget curl; do
	tryDownloadProgramPath="$(which "${tryDownloadProgram}" 2>/dev/null)"
	if [ -z "${tryDownloadProgramPath}" ]; then
		continue
	fi
	if [ -x "${tryDownloadProgramPath}" ]; then
		downloadProgram="${tryDownloadProgram}"
		downloadProgramPath="${tryDownloadProgramPath}"
		break
	fi
done
case "${hash}" in
	????????????????????????????????????????)
		hashMethod='sha1'
		;;
	????????????????????????????????????????????????????????????????)
		hashMethod='sha256'
		;;
	-)
		hashMethod='null'
		;;
	*)
		echo "Unknown hash method for hash ${hash}" >&2
		exit 1
		;;
esac
function downloadFile() {
	local file urls
	local downloadProgramArgs
	local url
	local authoritativeURL
	file="$1"
	shift
	urls=("$@")
	authoritativeURL="${urls[@]: -1}"
	case "${downloadProgram}" in
		curl)
			downloadProgramArgs=(--header "X-Cache-URL: ${authoritativeURL}" --location --insecure --fail --output "${file}")
			;;
		wget)
			downloadProgramArgs=(--header="X-Cache-URL: ${authoritativeURL}" --no-check-certificate --output-document="${file}")
			;;
	esac
	for url in "${urls[@]}" __fail__; do
		rm -f "${file}"
		if [ "${url}" = '__fail__' ]; then
			return 1
		fi
		"${downloadProgram}" "${downloadProgramArgs[@]}" "${url}" && break
	done
	return 0
}
function verifyHash() {
	local file hash hashMethod
	local checkHash
	file="$1"
	hash="$2"
	hashMethod="$3"
	if [ "${hashMethod}" = 'null' ]; then
		return 0
	fi
	checkHash="$(openssl dgst "-${hashMethod}" "${file}" | sed 's@.*= *@@')"
	if [ "${checkHash}" = "${hash}" ]; then
		return 0
	fi
	echo "Hash (${hashMethod}) mismatch: Got: ${checkHash}; Expected: ${hash}" >&2
	return 1
}
rm -f "${file}.new" || exit 1
urls=("${url}")
if [ "${hashMethod}" != 'null' ]; then
	urls=(
		"http://hashcache.rkeene.org/${hashMethod}/${hash}"
		"${urls[@]}"
	)
fi
downloadFile "${file}.new" "${urls[@]}" || exit 1
verifyHash "${file}.new" "${hash}" "${hashMethod}" || exit 1
mv "${file}.new" "${file}" || exit 1
exit 0