#!/bin/sh
## 
## Copyright 2015-2018 The Regents of the University of California
## All rights reserved.
## 
## This file is part of Spoofer.
## 
## Spoofer 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, either version 3 of the License, or
## (at your option) any later version.
## 
## Spoofer 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 Spoofer.  If not, see <http://www.gnu.org/licenses/>.
## 


stagedir=spoofer-stage
usage() {
    cat >&2 <<EOF
usage: $0 {-u|-s<dir>} [-r<cvsrev>] [-F] <version> {src|ubuntu|win|mac}...

Exports <cvs_revision> of spoofer from CVS into ${stagedir} and builds
one or more distributions.

Options:
-u            binary distributions will be unsigned
-s <dir>      binary distributions will be signed using files in <dir>
-r <cvsrev>   check out cvs revision "<cvsrev>" instead of "release-<version>"
              (with each '.' changed to '-').
-o <options>  additional options to pass to configure
-F            force version:  command line <version> overrides configure.ac
-t <tarball>  name of tarball file for "ubuntu" releases (defaults to tarball
              built by "src" argument)

<dist_type> is one or more of the following:
  src      source distribution
  ubuntu   Ubuntu PPA
  win      Windows binary distribution (requires a i686-w64-mingw32.static
           cross-compiling environment)
  mac      Mac OSX binary distribution (requires OSX)
EOF
}

DB_UPDATE=""

assert() {
    local cmd="$*"
    echo "#  $cmd" >&2
    if test "$1" = "!"; then
	shift
	! "$@"
    else
	"$@"
    fi || { echo; echo "$0: assertion failed: $cmd"; exit 1; } >&2
}

finish() {
    local dist_type="$1"
    local prefix="$2"
    local version="$3"
    local suffix="$4"
    local file="$prefix$version$suffix"
    (
	case "$version" in
	*alpha*|*beta*)
	    ;;
	*)
	    assert cp "./$file" ../web/downloads
	    assert cd ../web
	    assert cvs add -kb "downloads/$file"

	    # replace filename in index.xml
	    assert perl -0777 -pe "s/(href=[\"']downloads\\/\\Q${prefix}\\E)[^\\s<>]*(\\Q${suffix}\\E[\"']>\\s*\\Q${prefix}\\E)[^\\s<>]*(\\Q${suffix}\\E\\s*<.resource>)/\${1}${version}\${2}${version}\${3}/s;" index.xml >index.xml.tmp

	    assert ! cmp index.xml index.xml.tmp >/dev/null
	    assert mv index.xml.tmp index.xml
	    ;;
	esac

    ) || exit $?
    built="$built ${stagedir}/$(basename $(pwd))/$file"

    gen_db_update "$dist_type" "https://www.caida.org/projects/spoofer/downloads/${file}"
}

gen_db_update() {
    local dist_type="$1"
    local file="$2"
    case $dist_type in
    src)
	local WHERE="Os IS NULL AND UpgradeKey IS NULL"
	;;
    *)
	local OS=$(sed -ne '/^#define OS "\(.*\)"/s//\1/p' config.h)
	local UPGRADE_KEY=$(sed -ne '/^#define UPGRADE_KEY "\(.*\)"/s//\1/p' config.h)
	test -n "$UPGRADE_KEY" || return; # no automatic upgrade, no db update
	assert test -n "$OS"
	local WHERE="Os='${OS}' AND UpgradeKey='${UPGRADE_KEY}'"
	;;
    esac
    NVERSION=$(sed -ne '/^#define NVERSION \(.*\)/s//\1/p' config.h)
    DB_UPDATE="${DB_UPDATE}    UPDATE ClientVersions SET CurVersion=${NVERSION}, VersionStr='${version}', file='${file}' WHERE ${WHERE};
"
}

CONFIG_OPTIONS='--enable-prober --enable-manager'
revision=""
cs_dir="."
signed="signed"
signprefix=""

while test "$1"; do
    option=""
    case "$1" in
    -[rsot]) option=$1; optarg=$2; shift; shift;;
    -[rsot]*) option=$(echo "$1" | sed -e 's/^\(-.\).*/\1/'); optarg=$(echo "$1" | sed -e 's/^-.//'); shift;;
    -u) signed="unsigned"; signprefix="unsigned-"; shift;;
    -F) forceversion="1"; shift;;
    --) shift; break;;
    *) break;;
    esac
    case "$option" in
    -r) revision=$optarg;;
    -s) cs_dir=$optarg;;
    -o) CONFIG_OPTIONS="$CONFIG_OPTIONS $optarg";;
    -t) tarball="$optarg";;
    esac
done

version="$1"

test $# -ge 2 || { usage; exit 1; }
test -n "$version" || { usage; exit 1; }

shift

if test -z "$revision"; then
    revision="release-$(echo $version | sed 's/\./-/g')"
fi

for dist_type in "$@"; do
    case $dist_type in
    src|ubuntu|win|win32|mac|osx) ;;
    *)
	echo "Error: unknown distibution type \"$dist_type\"" >&2
	exit 1
	;;
    esac
done

# make cs_dir absolute
cs_dir=$(cd "$cs_dir"; pwd;)

echo "# release version: $version"
echo "# release revision: $revision"
echo "# release types: $*"

if test $(umask) != "0022"; then
    echo "WARNING: umask is $(umask); normally it should be 0022."
    echo "Press return to continue."
    read line
fi

exportdir="spoofer-$revision"
if test -d $stagedir/$exportdir; then
    echo "############## Using existing $stagedir/$exportdir"
    echo "Press return to continue."
    read dummy
    assert cd $stagedir/$exportdir
else
    echo "############## Exporting $stagedir/$exportdir"
    chmod -R u+rw $stagedir
    assert rm -rf $stagedir
    assert mkdir $stagedir
    assert cd $stagedir
    assert cvs export -r $revision -d $exportdir WIP/spoofer
    case "$version" in
    *alpha*|*beta*)
	# don't need web directory
	;;
    *)
	assert cvs checkout -d web www-caida-org/new-projects/spoofer
	;;
    esac
    assert cd $exportdir
fi

case "$version" in
*alpha*)
    cat README.alpha README >README.tmp && mv README.tmp README;;
esac

# example:  m4_define([SPOOFER_VERSION], [1.4.0-beta1])
cfgversion=$(sed -ne 's/^m4_define(\[SPOOFER_VERSION\], *\[\([^]]*\)\].*/\1/p' configure.ac)
if test "$forceversion" = "1"; then
    sed -e 's/^\(m4_define(\[SPOOFER_VERSION\], *\)\[\([^]]*\)\]/\1['"$version"']/' configure.ac >configure-forced.ac || exit $?
    echo "Forced version:"
    diff configure.ac configure-forced.ac
    mv configure-forced.ac configure.ac
elif test "x$cfgversion" != "x$version"; then
    echo "ERROR: configure.ac version '$cfgversion' does not match requested version '$version'."
    exit 1
fi

assert ./bootstrap
assert cd .. ; # ${stagedir}
:>NEXT_STEPS

built=""

for dist_type in "$@"; do
    case $dist_type in
    src)
	echo; echo "############## building source distribution"
	assert rm -rf src
	assert mkdir src
	assert cd src
	if test -z "$QMAKE"; then
	    for f in qmake "$HOME/Qt/5.3/gcc/bin/qmake"; do
		if $f --version >/dev/null 2>&1; then QMAKE="$f"; break; fi
	    done
	fi
	if test -z "$PROTOC"; then
	    for f in protoc "$HOME/proto3/bin/protoc"; do
		if $f --version >/dev/null 2>&1; then PROTOC="$f"; break; fi
	    done
	fi
	assert ../$exportdir/configure ${CONFIG_OPTIONS}
	# During "make dist", disable unneeded tools to catch unintended use.
	assert make dist CXX='bad_CXX' PROTOC='bad_PROTOC' QMAKE='bad_QMAKE'
	assert make distcheck DISTCHECK_CONFIGURE_FLAGS="${CONFIG_OPTIONS} \
	    PROTOC='$PROTOC' QMAKE='$QMAKE'"
	assert finish $dist_type spoofer- "$version" .tar.gz
	assert cd ..; # ${stagedir}
	;;

    ubuntu)
	# See: https://help.launchpad.net/Packaging/PPA/BuildingASourcePackage
	echo; echo "############## building ubuntu distribution"
	assert test "$(lsb_release -si)" = "Ubuntu"
	assert rm -rf ubuntu
	assert mkdir ubuntu
	assert cd ubuntu

	case "$tarball" in
	    '') tarball=../src/spoofer-$version.tar.gz;;
	    /*) : nop ;;
	    *) tarball=../../$tarball;;
	esac
	assert ln -s "$tarball" spoofer_$version-1~ubuntu.orig.tar.gz
	assert tar zxf "$tarball"

	assert cd spoofer-$version
	if test -n "$SPOOFER_DEBIAN"; then
	    cp -a "$SPOOFER_DEBIAN" debian; # XXX
	else
	    assert cvs checkout -d debian WIP/spoofer/debian
	fi

	# sanity check copyright
	egrep "The Regents of the University of California" debian/copyright>reg
	assert egrep -q "$(date +%Y) The Regents" reg
	egrep -v "$(date +%Y) The Regents" reg && \
	    { echo "Error: out of date debian/copyright?" >&2; exit 1; }
	rm reg

	cat >>../../NEXT_STEPS <<-EOF
	* locally:
	    in '$stagedir/ubuntu'
	        cvs commit spoofer-$version/debian
	EOF

	all_series="xenial bionic"
	if test -n "$SPOOFER_RELEASE_BINARY"; then
	    all_series=$(lsb_release -sc)
	fi
	prev_series=""
	for series in $all_series; do
	    case $series in
		xenial) ubuntu="ubuntu-16.04.1";;
		bionic) ubuntu="ubuntu-18.04.1";;
		*) echo "unknown ubuntu series '$series'" >&2; exit 1;;
	    esac

	    if test -z "$prev_series"; then
		assert debchange -p -v "$version-1~$ubuntu" -D "$series"
	    else
		sed -i -e "1s/~ubuntu.*) $prev_series;/~$ubuntu) $series;/" \
		    debian/changelog
	    fi
	    PPA_VERSION=$(sed -ne '1s/spoofer (\(.*\)).*/\1/p' debian/changelog)

	    # Note: to check dependencies, build in a clean chroot environment
	    # with: `pdebuild --buildresult ..`
	    if test -n "$SPOOFER_RELEASE_BINARY"; then
		assert debuild -i -us -uc -b
	    else
		assert debuild -S
	    fi

	    built="$built ${stagedir}/ubuntu/spoofer_${PPA_VERSION}"'*'
	    echo >>../../NEXT_STEPS "        dput spoofer-$series" \
		"spoofer_${PPA_VERSION}_source.changes"
	    prev_series="$series"
	done

	sed -i \
	    -e "s/matthewluckie\//spoofer-dev\//" \
	    -e "s/\(Spoofer-\)[^ ]*\( PPA\)/Spoofer-$version PPA/" \
	    -e "s/Ubuntu.*Package.*(signed)/Ubuntu Packages for: $all_series (signed)/" \
	    ../../web/index.xml

	cd ../..; # $stagedir
	;;

    win|win32)
	echo; echo "############## building $signed Windows binary distribution"
	assert rm -rf win32
	assert mkdir win32
	assert cd win32
	assert cp $HOME/WIP/spoofer/win-bin/WinPcap_4_1_3.exe ../spoofer/win-bin
	assert cp $HOME/WIP/spoofer/win-bin/vc2012u4redist_x86.exe ../spoofer/win-bin
	assert cp $HOME/WIP/spoofer/win-bin/scamper.exe ../spoofer/win-bin
	(
	    PATH=/opt/mxe/usr/bin:$PATH
	    assert ../$exportdir/configure ${CONFIG_OPTIONS} \
		--host=i686-w64-mingw32.static \
		UPGRADE_KEY="https://www.caida.org/" \
		CXXFLAGS="-O2 -DNDEBUG" \
		PROTOC=$HOME/proto3/bin/protoc \
		--with-protobuf=$HOME/proto3-win32
	    assert make CS_DIR="$cs_dir" windist-${signed}
	) || exit $?
	assert finish $dist_type ${signprefix}Spoofer- "$version" -win32.exe
	assert cd ..; # ${stagedir}
	;;

    mac|osx)
	echo; echo "############## building $signed Mac OSX binary distribution"
	assert test $(uname) = "Darwin"
	assert rm -rf mac
	assert mkdir mac
	assert cd mac
	(
	    # where to find headers/libraries/tools/etc for deployment target
	    TARGETROOT="$HOME/macos-10.7"
	    # set PATH for protoc, productsign, qmake
	    PATH="$TARGETROOT/bin:$PATH"
	    export MACOSX_DEPLOYMENT_TARGET=10.7
	    assert ../$exportdir/configure \
		--with-openssl=/usr/local/Cellar/openssl/1.0.2l \
		${CONFIG_OPTIONS} \
		UPGRADE_KEY="https://www.caida.org/" \
		CXX='clang++ -stdlib=libc++' \
		CXXFLAGS="-arch x86_64 -O2 -I$TARGETROOT/include -DNDEBUG" \
		LDFLAGS="-arch x86_64 -L$TARGETROOT/lib"
		# PKG_CONFIG_PATH="$TARGETROOT/lib/pkgconfig" # not needed
	    assert make macdist-${signed}
	) || exit $?
	assert finish $dist_type ${signprefix}Spoofer- "$version" -macosx.pkg
	assert cd ..; # ${stagedir}
	;;

    *)
	# shouldn't happen
	echo "############## unknown distibution type \"$dist_type\"" >&2
	exit 1
	;;
    esac
done

case "$version" in
*alpha*|*beta*)
    ;;
*)
    assert cp $exportdir/CHANGES web/downloads/changelog.txt
    assert cd web
    echo; echo
    cvs status 2>&1 | egrep '^File:|^cvs status: Examining' | egrep -v 'Status: Up-to-date'
    cd ..
    ;;
esac
echo; echo
echo "Successfully built releases:"
for file in $built; do echo "    $file"; done

case "$version" in
*alpha*|*beta*)
    ;;
*)
    echo
    echo 'Next steps:'
    cat NEXT_STEPS
    echo '* locally:'
    echo "    in $stagedir/web"
    echo "        cvs commit"
    echo '* on cider:'
    echo "    cd ~/private_xml/www-caida-org/projects/spoofer"
    echo "    cvs update"
    echo "    # check https://www-xml.caida.org/~${USER}/www-caida-org/projects/spoofer"
    echo "    wcs pub"
    if test -n "$DB_UPDATE"; then
	echo '* in database:'
	echo "$DB_UPDATE"
    fi
    ;;
esac
