#! /usr/bin/env bash
# f24353d02d90f97c72b6977f3a8a05006ad06120
ourScript="$(which "$0")"
if ! head -3 "${ourScript}" 2>/dev/null | grep f24353d02d90f97c72b6977f3a8a05006ad06120 >/dev/null; then
	echo "error: Unable to find ourselves" >&2
	exit 1
fi
cd "$(dirname "${ourScript}")" || exit 1
patchDir="$(pwd)/patches"
if [ -z "${NACL_SDK_PEPPER}" ]; then
	echo "error: Please set NACL_SDK_PEPPER to the path of the current pepper target" >&2
	exit 1
fi
function download() {
	local url file hash
	local hashMethod
	local chkHash
	url="$1"
	file="$2"
	hash="$3"
	if [ -f "${file}" ]; then
		return 0
	fi
	mkdir -p "$(dirname "${file}")"
	hashMethod='sha256'
	rm -f "${file}.new"
	wget --header "X-Cache-URL: ${url}" -O "${file}.new" "http://hashcache.rkeene.org/${hashMethod}/${hash}" || \
		wget -O "${file}.new" "${url}" || \
		return 1
	chkHash="$(openssl "${hashMethod}" "${file}.new" | sed 's@.*= *@@')"
	if [ "${chkHash}" != "${hash}" ]; then
		echo "error: Checksum mismatch: Got: ${chkHash}; Expected: ${hash}" >&2
		return 1
	fi
	mv "${file}.new" "${file}"
	return 0
}
function extract() {
	local file directory
	file="$1"
	directory="$2"
	if [ ! -f "${file}" ]; then
		echo "error: Unable to extract \"${file}\"" >&2
		return 1
	fi
	rm -rf "${directory}"
	mkdir -p "${directory}" || return 1
	(
		cd "${directory}" || exit 1
		case "${file}" in
			*.tar.bz2|*.bz2)
				bzip2 -dc | tar -xf - || exit 1
				;;
			*.tar.gz|*.tgz)
				gzip -dc | tar -xf - || exit 1
				;;
			*.tar.xz|*.txz)
				xz -dc | tar -xf - || exit 1
				;;
			*)
				echo "error: Don't know what to do with \"${file}\"" >&2
				exit 1
				;;
		esac
		if [ -d "$(echo *)" ]; then
			mv */* . >/dev/null 2>/dev/null
		fi
	) < "${file}" || return 1
	return 0
}
function make() {
	"${MAKE:-$(which make)}" "$@"
}
function buildLibUSB() {
	local version url pkg sha256 configure_extra
	local archive workdir instdir
	pkg='libusb'
	version='1.0.9'
	url="http://sourceforge.net/projects/libusb/files/libusb-1.0/libusb-${version}/libusb-${version}.tar.bz2"
	sha256='e920eedc2d06b09606611c99ec7304413c6784cba6e33928e78243d323195f9b'
	configure_extra=(--disable-shared --enable-static --with-pic "${global_configure_extra[@]}")
	archive="archive/${pkg}-${version}.tar.bz2"
	workdir="workdir-${RANDOM}${RANDOM}${RANDOM}${RANDOM}.build"
	instdir="workdir-${RANDOM}${RANDOM}${RANDOM}${RANDOM}.inst"
	download "${url}" "${archive}" "${sha256}" || return 1
	extract "${archive}" "${workdir}" || return 1
	(
		mkdir "${instdir}"
		instdir="$(cd "${instdir}" && pwd)" || exit 1
		cd "${workdir}" || exit 1
		./configure --prefix='' "${configure_extra[@]}" || exit 1
		make V=1 || exit 1
		make V=1 DESTDIR="${instdir}" install || exit 1
	) || return 1
	rm -rf "${workdir}"
	cackeyChromeExtLibUSBDir="$(cd "${instdir}" && pwd)" || return 1
	export LIBUSB_LIBS="-L${cackeyChromeExtLibUSBDir}/lib -lusb-1.0 -lrt -pthread"
	export LIBUSB_CFLAGS="-I${cackeyChromeExtLibUSBDir}/include/libusb-1.0"
	return 0
}
function buildCCID() {
	local version url pkg sha256 configure_extra
	local archive workdir instdir
	pkg='ccid'
	version='1.4.22'
	url="https://alioth.debian.org/frs/download.php/file/4162/ccid-${version}.tar.bz2"
	sha256='9c5c8be465b6d33316be7a7ea720c24a776e2d76be9072116d28fc9adf58c106'
	configure_extra=(
		--with-pic
		--disable-shared --enable-static --enable-usb
		--enable-usbdropdir=/drivers
		"${global_configure_extra[@]}"
	)
	archive="archive/${pkg}-${version}.tar.bz2"
	workdir="workdir-${RANDOM}${RANDOM}${RANDOM}${RANDOM}.build"
	instdir="workdir-${RANDOM}${RANDOM}${RANDOM}${RANDOM}.inst"
	download "${url}" "${archive}" "${sha256}" || return 1
	extract "${archive}" "${workdir}" || return 1
	(
		mkdir "${instdir}"
		instdir="$(cd "${instdir}" && pwd)" || exit 1
		cd "${workdir}" || exit 1
		./configure --prefix='' "${configure_extra[@]}" || exit 1
		make V=1 || exit 1
		mkdir "${instdir}/lib"
		cp src/.libs/libccid.a "${instdir}/lib"
		mkdir "${instdir}/include"
		./src/create_Info_plist.pl ./readers/supported_readers.txt src/Info.plist.src --target=libccid.so --version=1.0.0 | \
			sed '/<key>ifdDriverOptions/ {n;c \'$'\n''<string>0x0004</string>'$'\n''p}' | \
			od -t x1 -v | sed 's@^[^ ]* @@;s@ @@g;s@..@\\x&@g;$ d;s@^@"@;s@$@"@' > "${instdir}/include/Info.plist.h"
	) || return 1
	rm -rf "${workdir}"
	cackeyChromeExtCCIDDir="$(cd "${instdir}" && pwd)" || return 1
	return 0
}
function buildPCSCLite() {
	local version url pkg sha256 configure_extra
	local archive workdir instdir
	buildLibUSB || return 1
	pkg='pcsc-lite'
	version='1.8.15'
	url="https://alioth.debian.org/frs/download.php/file/4157/pcsc-lite-${version}.tar.bz2"
	sha256='ad8b1f3d2c59d3a966cb203fc74588629c4a5fa30f8ad9005e06ef7aa445d341'
	configure_extra=(
		--disable-shared --enable-static --enable-usb --disable-libudev --disable-polkit --with-pic
		"${global_configure_extra[@]}"
	)
	archive="archive/${pkg}-${version}.tar.bz2"
	workdir="workdir-${RANDOM}${RANDOM}${RANDOM}${RANDOM}.build"
	instdir="workdir-${RANDOM}${RANDOM}${RANDOM}${RANDOM}.inst"
	download "${url}" "${archive}" "${sha256}" || return 1
	extract "${archive}" "${workdir}" || return 1
	(
		mkdir "${instdir}"
		instdir="$(cd "${instdir}" && pwd)" || exit 1
		cd "${workdir}" || exit 1
		./configure --prefix='' --sbindir="/bin" "${configure_extra[@]}" || exit 1
		make V=1 || exit 1
		make V=1 DESTDIR="${instdir}" install || exit 1
	) || return 1
	cackeyChromeExtPCSCLiteDir="$(cd "${instdir}" && pwd)"
	export PCSC_CFLAGS="-I${cackeyChromeExtPCSCLiteDir}/include/PCSC"
	export PCSC_LIBS="-L${cackeyChromeExtPCSCLiteDir}/lib -lpcsclite -pthread"
	buildCCID || return 1
	rm -rf "${instdir}"
	unset PCSC_CFLAGS PCSC_LIBS cackeyChromeExtPCSCLiteDir
	rm -rf "${workdir}"
	extract "${archive}" "${workdir}" || return 1
	(
		mkdir "${instdir}"
		instdir="$(cd "${instdir}" && pwd)" || exit 1
		cd "${workdir}" || exit 1
		sed -i 's@^pcscd_LDFLAGS[[:space:]]*=@& -all-static @' src/Makefile.in
		for patchFile in "${patchDir}"/pcsc-lite-*.diff; do
			if [ ! -f "${patchFile}" ]; then
				continue
			fi
			patch -p1 < "${patchFile}" || exit 1
		done
		cp "${cackeyChromeExtCCIDDir}/include/Info.plist.h" src/ || exit 1
		extraDefines='-DPCSCLITE_STATIC_DRIVER -DIFDHANDLERv3'
		./configure --prefix='' --sbindir="/bin" "${configure_extra[@]}" CFLAGS="-g3 -ggdb3 ${CFLAGS} ${extraDefines}" CPPFLAGS="${CPPFLAGS} ${extraDefines}" LIBS="${LIBS} -L${cackeyChromeExtCCIDDir}/lib -lccid" || exit 1
		make V=1 || exit 1
		make V=1 DESTDIR="${instdir}" install || exit 1
	) || return 1
	rm -rf "${cackeyChromeExtLibUSBDir}"
	unset LIBUSB_LIBS LIBUSB_CFLAGS
	rm -rf "${cackeyChromeExtCCIDDir}"
	rm -rf "${workdir}"
	cackeyChromeExtPCSCLiteDir="$(cd "${instdir}" && pwd)"
	export PCSC_CFLAGS="-I${cackeyChromeExtPCSCLiteDir}/include/PCSC"
	export PCSC_LIBS="-L${cackeyChromeExtPCSCLiteDir}/lib/dummy -lpcsclite"
	# Create dummy PC/SC library -- just enough to past CACKey linking test
	mkdir "${cackeyChromeExtPCSCLiteDir}/lib/dummy" || return 1
	echo 'void SCardEstablishContext(void) { return; }; void SCardConnect(void) { return; }' | gcc -x c - -fPIC -shared -o "${cackeyChromeExtPCSCLiteDir}/lib/dummy/libpcsclite.so"
	# Copy statically linked "pcscd" out
	cp "${cackeyChromeExtPCSCLiteDir}/bin/pcscd" "${outdir}" || return 1
	return 0
}
function buildCACKey() {
	local platform
	local file copied
	platform="$1"
	shift
	if [ -z "${platform}" ]; then
		echo 'error: Platform not specified' >&2
		return 1
	fi
	(
		cd ../.. || exit 1
		make distclean
		./configure --with-pcsc-headers="${cackeyChromeExtPCSCLiteDir}/include/PCSC" --with-pcsc-libs="${PCSC_LIBS}" "$@" || exit 1
		make || exit 1
	) || return 1
	copied='0'
	for file in ../../libcackey{,_g}.{so,dll,dylib}; do
		if [ -f "${file}" ]; then
			mkdir -p "${outdir}/${platform}"
			cp "${file}" "${outdir}/${platform}"
			copied='1'
		fi
	done
	if [ "${copied}" = '0' ]; then
		echo "error: Unable to copy built libcackey to local directory" >&2
		return 1
	fi
	return 0
}
function cleanup() {
	if [ -n "${cackeyChromeExtPCSCLiteDir}" ]; then
		rm -rf "${cackeyChromeExtPCSCLiteDir}"
		unset PCSC_CFLAGS PCSC_LIBS
	fi
	if [ -n "${cackeyChromeExtLibUSBDir}" ]; then
		rm -rf "${cackeyChromeExtLibUSBDir}"
		unset LIBUSB_LIBS LIBUSB_CFLAGS
	fi
}
outdir="workdir-${RANDOM}${RANDOM}${RANDOM}${RANDOM}.out"
rm -rf "${outdir}"
mkdir "${outdir}" || exit 1
buildPCSCLite || exit 1
buildCACKey build || exit 1
buildOutputType="$(echo 'int main(int argc, char **argv) { return(0); }' | "${CC:-cc}" -x c - -o /dev/stdout | file -)" || exit 1
case "${buildOutputType}" in
	*x86-64*)
		naclTopDir='linux_x86_glibc'
		naclPlatform='x86_64-nacl'
		;;
	*)
		echo "error: Unrecognized platform output: \"${buildOutputType}\"" >&2
		exit 1
		;;
esac
PATH="${PATH}:${NACL_SDK_PEPPER}/toolchain/${naclTopDir}/bin" buildCACKey "${naclPlatform}" --host="${naclPlatform}" CC=${naclPlatform}-gcc LD=${naclPlatform}-ld OBJCOPY=${naclPlatform}-objcopy STRIP=${naclPlatform}-strip || exit 1
cleanup
exit 0