#!/bin/sh
# Copyright 2009 Palm, Inc.  All rights reserved.

# NOTE: this script is not reentrant.
# NOTE: this script resides in component DownloadUpdate (for device) and Scripts (for build server)
#       When making changes please notify the respective owners about the modifications

# Turn on tracing
#set -x

export LC_ALL=POSIX

realpath() {
	path=$1
	case "$path" in
	/*)
		echo $path
		;;
	*)
		echo "`pwd`/$path"
		;;
	esac
}

cleanup() {
	echo "path ${1}"
   	if [ "${1}" ]
   	then
   		echo "path ${1}"
   		rm -rf ${1}
   	fi
}

usage() {
	echo "$0: diff <old.ipk> <new.ipkg> <patch.dipkg>             # constructs patch to turn old into new"
	echo "$0: diff -r <rootfs> <pkgname> <new.ipkg> <patch.dipkg> # constructs patch using rootfs for old data"
	echo "$0: patch <old.ipk> <new.ipkg> <patch.dipkg>            # reconstructs new.ipkg from old and patch"
	echo "$0: patch -r <rootfs> <pkgname> <new.ipkg> <patch.dipkg> <temppath> # reconstructs new.ipkg from rootfs and patch"
	echo "$0: id <.ipkg>                                          # gives identifying hash for package"
	echo "$0: id -r <rootfs> <pkgname>                            # gives identifying hash for installed package"
	echo "$0: compare <old.ipk> <new.ipk>                         # compares two package files for equivalence"
	echo "$0: integcheck -r <rootfs> <new.ipk>                    # verifies package is installed in rootfs"
	exit 1
}

if [ $# -eq 4 -a "x$1" = "xdiff" -a -f "$2" -a -f "$3" ] ; then
	
	oldipk=`realpath $2`
	newipk=`realpath $3`
	patchipk=`realpath $4`

	# FIXME: using a statically named temporary directory makes
	# this script nonreentrant.
	rm -rf /tmp/ipkgd
	
	old=/tmp/ipkgd/old-ipk
	new=/tmp/ipkgd/new-ipk
	
	mkdir -p $old/c $old/d $new/c $new/d
	
	# unpack old archive	
	(cd $old ; ar x $oldipk ; gunzip control.tar.gz ; gunzip data.tar.gz ; tar xfC control.tar c ; tar xfC data.tar d)

	# unpack new archive	
	(cd $new ; ar x $newipk ; gunzip control.tar.gz ; gunzip data.tar.gz)
	
	# if md5sums is empty, then the rest of this is useless so just bail now.
	if [ ! -e ${old}/c/md5sums -o ! -s ${old}/c/md5sums ] ; then
	    exit 0
	fi

	# construct list of old data files
	cat $old/c/md5sums | sed 's/^.* \*//' | sort > /tmp/ipkgd/orig-data.list
	
	# construct concatenated old data
	(cd $old/d ; while read file; do cat "$file"; done < /tmp/ipkgd/orig-data.list) > /tmp/ipkgd/orig-data.cat
	
	# construct md5sum of old file data
	md5sum /tmp/ipkgd/orig-data.cat | sed 's/ .*//' > /tmp/ipkgd/orig-data.md5sum
	
	# construct md5sum of old control files
	md5sum $old/control.tar| sed 's/ .*//' > /tmp/ipkgd/orig-control.md5sum
	
	# construct md5sum of new data files
	md5sum $new/data.tar| sed 's/ .*//' > /tmp/ipkgd/new-data.md5sum

	# construct md5sum of new control files
	md5sum $new/control.tar| sed 's/ .*//' > /tmp/ipkgd/new-control.md5sum
	
	cp /tmp/ipkgd/orig-control.md5sum $new/
	cp /tmp/ipkgd/new-control.md5sum $new/
	cp /tmp/ipkgd/orig-data.md5sum $new/
	cp /tmp/ipkgd/new-data.md5sum $new/
	
	bsdiff /tmp/ipkgd/orig-data.cat $new/data.tar $new/data.patch.bsdiff
	
	rm -rf $new/c $new/d $new/data.tar
	
	rm -f $patchipk
	(cd $new ; gzip -9 control.tar ; ar cq $patchipk *)

	# rm -rf /tmp/ipkgd
	
	exit 0

elif [ $# -eq 6 -a "x$1" = "xdiff" -a "x$2" = "x-r" -a -d "$3" -a -f "$5" ] ; then
	
	root=`realpath $3`
	ipkname="$4"
	newipk=`realpath $5`
	patchipk=`realpath $6`

	rm -rf /tmp/ipkgd

	new=/tmp/ipkgd/new-ipk

	mkdir -p $new/c $new/d

	# construct list of old data files
	cat $root/usr/lib/ipkg/info/${ipkname}.md5sums | sed 's/^.* \*//' | sort > /tmp/ipkgd/orig-data.list

	# construct concatenated old data
	(cd $root ; while read file; do cat "$file"; done < /tmp/ipkgd/orig-data.list) > /tmp/ipkgd/orig-data.cat

	# construct md5sum of old file data
	md5sum /tmp/ipkgd/orig-data.cat | sed 's/ .*//' > /tmp/ipkgd/orig-data.md5sum

	# unpack new archive	
	(cd $new ; ar x $newipk ; gunzip control.tar.gz ; gunzip data.tar.gz)
	
	# construct md5sum of new data files
	md5sum $new/data.tar| sed 's/ .*//' > /tmp/ipkgd/new-data.md5sum

	# construct md5sum of new control files
	md5sum $new/control.tar| sed 's/ .*//' > /tmp/ipkgd/new-control.md5sum
	
	cp /tmp/ipkgd/orig-control.md5sum $new/
	cp /tmp/ipkgd/new-control.md5sum $new/
	cp /tmp/ipkgd/orig-data.md5sum $new/
	cp /tmp/ipkgd/new-data.md5sum $new/
	
	bsdiff /tmp/ipkgd/orig-data.cat $new/data.tar $new/data.patch.bsdiff
	
	rm -rf $new/c $new/d $new/data.tar
	
	rm -f $patchipk
	(cd $new ; gzip -9 control.tar ; ar cq $patchipk *)

	# rm -rf /tmp/ipkgd
	
	exit 0

elif [ $# -eq 4 -a "x$1" = "xpatch" -a -f "$2" -a -f "$4" ] ; then
	
	oldipk=`realpath $2`
	newipk=`realpath $3`
	patchipk=`realpath $4`

	rm -rf /tmp/ipkgd
	
	old=/tmp/ipkgd/old-ipk
	patch=/tmp/ipkgd/patch-ipk
	
	mkdir -p $old/c $old/d $patch
	
	# unpack old archive	
	(cd $old ; ar x $oldipk ; gunzip control.tar.gz ; gunzip data.tar.gz ; tar xfC control.tar c ; tar xfC data.tar d)

	# unpack new archive	
	(cd $patch ; ar x $patchipk ; gunzip control.tar.gz )
	
	if [	! -f "$patch/orig-data.md5sum" -o \
		! -f "$patch/orig-control.md5sum" -o \
		! -f "$patch/new-data.md5sum" -o \
		! -f "$patch/new-control.md5sum" -o \
		! -f "$patch/control.tar" -o \
		! -f "$patch/data.patch.bsdiff" ] ; then
		echo "Patch archive $patchipk is missing critical files"
		exit 2
	fi
	
	# construct list of old data files
	cat $old/c/md5sums | sed 's/^.* \*//' | sort > /tmp/ipkgd/orig-data.list
	
	# construct concatenated old data
	(cd $old/d ; while read file; do cat "$file"; done < /tmp/ipkgd/orig-data.list) > /tmp/ipkgd/orig-data.cat

	# construct md5sum of old file data
	md5sum /tmp/ipkgd/orig-data.cat | sed 's/ .*//' > /tmp/ipkgd/orig-data.md5sum

	# construct md5sum of old control files
	md5sum $old/control.tar | sed 's/ .*//' > /tmp/ipkgd/orig-control.md5sum
	
	if ! cmp -s /tmp/ipkgd/orig-data.md5sum $patch/orig-data.md5sum ; then
		echo "Old archive data md5 does not match patch archive"
		exit 3
	fi
	
	if ! cmp -s /tmp/ipkgd/orig-control.md5sum $patch/orig-control.md5sum ; then
		echo "Old archive control md5 does not match patch archive"
		exit 4
	fi
	
	bspatch /tmp/ipkgd/orig-data.cat $patch/data.tar $patch/data.patch.bsdiff

	# construct md5sum of new data files
	md5sum $patch/data.tar| sed 's/ .*//' > /tmp/ipkgd/new-data.md5sum

	# construct md5sum of new control files
	md5sum $patch/control.tar| sed 's/ .*//' > /tmp/ipkgd/new-control.md5sum
	
	if ! cmp -s /tmp/ipkgd/new-data.md5sum $patch/new-data.md5sum ; then
		echo "Reconstructed archive data md5 does not match patch archive"
		exit 5
	fi
	
	if ! cmp -s /tmp/ipkgd/new-control.md5sum $patch/new-control.md5sum ; then
		echo "Reconstructed archive control md5 does not match patch archive"
		exit 6
	fi
	
	(cd $patch ; gzip -9 control.tar ; gzip -9 data.tar )
	
	rm -f $patch/data.patch.bsdiff
	rm -f $patch/new-data.md5sum
	rm -f $patch/orig-data.md5sum
	rm -f $patch/new-control.md5sum
	rm -f $patch/orig-control.md5sum

	rm -f $newipk
	(cd $patch ; ar cq $newipk *)
	
	# rm -rf /tmp/ipkgd
	
	exit 0

elif [ $# -eq 7 -a "x$1" = "xpatch" -a "x$2" = "x-r" -a -d "$3" -a -f "$6" ] ; then
	
	root=`realpath $3`
	ipkname="$4"
	newipk=`realpath $5`
	patchipk=`realpath $6`
	tmp="/tmp"
	
	if [ ${7} != "" ] ;
	then
		tmp=${7}
	fi
	
	rm -rf ${tmp}/ipkgd
	
	patch=${tmp}/ipkgd/patch-ipk
	
	mkdir -p $patch
	
	# unpack new archive	
	(cd $patch ; ar x $patchipk ; gunzip control.tar.gz )
	
	if [	! -f "$patch/orig-data.md5sum" -o 	\
		! -f "$patch/orig-control.md5sum" -o	\
		! -f "$patch/new-data.md5sum" -o	\
		! -f "$patch/new-control.md5sum" -o	\
		! -f "$patch/control.tar" -o	\
		! -f "$patch/data.patch.bsdiff" ] ; then
		echo "Patch archive $patchipk is missing critical files"
		cleanup ${tmp}/ipkgd
		exit 2
	fi
	
	# construct list of old data files
	cat $root/usr/lib/ipkg/info/${ipkname}.md5sums | sed 's/^.* \*//' | sort > ${tmp}/ipkgd/orig-data.list
	
	# construct concatenated old data
	(cd $root ; while read file; do cat "$file"; done < ${tmp}/ipkgd/orig-data.list) > ${tmp}/ipkgd/orig-data.cat
	
	# construct md5sum of old file data
	md5sum ${tmp}/ipkgd/orig-data.cat | sed 's/ .*//' > ${tmp}/ipkgd/orig-data.md5sum
	
	# construct (local) list of old control files
	# ls $root/usr/lib/ipkg/info/${ipkname%.*}.* | grep -v '\.list$' | sort > ${tmp}/ipkgd/orig-control.list

	if ! cmp -s ${tmp}/ipkgd/orig-data.md5sum $patch/orig-data.md5sum ; then
		echo "Old ipkg in root data md5 does not match patch archive"
		cleanup ${tmp}/ipkgd
		exit 3
	fi
	
	bspatch ${tmp}/ipkgd/orig-data.cat $patch/data.tar $patch/data.patch.bsdiff

	# construct md5sum of new data files
	md5sum $patch/data.tar| sed 's/ .*//' > ${tmp}/ipkgd/new-data.md5sum

	# construct md5sum of new control files
	md5sum $patch/control.tar| sed 's/ .*//' > ${tmp}/ipkgd/new-control.md5sum
	
	if ! cmp -s ${tmp}/ipkgd/new-data.md5sum $patch/new-data.md5sum ; then
		echo "Reconstructed archive data md5 does not match patch archive"
		cleanup ${tmp}/ipkgd
		exit 5
	fi
	
	if ! cmp -s ${tmp}/ipkgd/new-control.md5sum $patch/new-control.md5sum ; then
		echo "Reconstructed archive control md5 does not match patch archive"
		cleanup ${tmp}/ipkgd
		exit 6
	fi
	
	(cd $patch ; gzip -9 control.tar ; gzip -9 data.tar )
	
	rm -f $patch/data.patch.bsdiff
	rm -f $patch/new-data.md5sum
	rm -f $patch/orig-data.md5sum
	rm -f $patch/new-control.md5sum
	rm -f $patch/orig-control.md5sum

	rm -f $newipk
	cd $patch 
	ar cq $newipk * 2>/dev/null
	if [ $? -ne 0 ]
	then 
		echo "Archive failure"
		cleanup ${tmp}/ipkgd
		exit 7		 
	fi
	
	rm -rf ${tmp}/ipkgd
	
	exit 0

elif [ $# -eq 2 -a "x$1" = "xid" -a -f "$2" ] ; then
	
	oldipk=`realpath $2`

	rm -rf /tmp/ipkgd
	
	old=/tmp/ipkgd/old-ipk
	
	mkdir -p $old/c $old/d
	
	# unpack old archive	
	(cd $old ; ar x $oldipk ; gunzip control.tar.gz ; gunzip data.tar.gz ; tar xfC control.tar c ; tar xfC data.tar d)

	# construct list of old control files
	(cd $old/c ; find . -type f ) | sort > /tmp/ipkgd/orig-control.list
	
	# construct concatenated old control files
	(cd $old/c ; cat `cat /tmp/ipkgd/orig-control.list`) > /tmp/ipkgd/orig-control.cat
	
	# construct md5sum of old control files
	md5sum /tmp/ipkgd/orig-control.cat | sed 's/ .*//' > /tmp/ipkgd/orig-control.md5sum
	
	cat /tmp/ipkgd/orig-control.md5sum

	# rm -rf /tmp/ipkgd
	
	exit 0

elif [ $# -eq 4 -a "x$1" = "xid" -a "x$2" = "x-r" -a -d "$3" ] ; then
	
	root=`realpath $3`
	ipkname="$4"
	
	rm -rf /tmp/ipkgd
	mkdir /tmp/ipkgd
	
	# construct (local) list of old control files
	ls $root/usr/lib/ipkg/info/${ipkname}.* | grep -v '\.list$' | sort > /tmp/ipkgd/orig-control.list

	# construct concatenated old control files
	(cat `cat /tmp/ipkgd/orig-control.list`) > /tmp/ipkgd/orig-control.cat
	
	# construct md5sum of old control files
	md5sum /tmp/ipkgd/orig-control.cat | sed 's/ .*//' > /tmp/ipkgd/orig-control.md5sum
	
	cat /tmp/ipkgd/orig-control.md5sum

	# rm -rf /tmp/ipkgd

	exit 0

elif [ $# -eq 3 -a "x$1" = "xcompare" -a -f "$2" -a -f "$3" ] ; then
	
	oldipk=`realpath $2`
	newipk=`realpath $3`

	rm -rf /tmp/ipkgd
	
	old=/tmp/ipkgd/old-ipk
	new=/tmp/ipkgd/new-ipk
	
	mkdir -p $old/c $old/d $new/c $new/d
	
	# unpack old archive	
	(cd $old ; ar x $oldipk ; gunzip control.tar.gz ; gunzip data.tar.gz)

	# unpack new archive	
	(cd $new ; ar x $newipk ; gunzip control.tar.gz ; gunzip data.tar.gz)
	
	# Compare data.tar, control.tar, and any loose files (debian-binary, etc.)
	
	(cd $old ; md5sum `find . -type f` | (cd $new ; md5sum -c))

	# rm -rf /tmp/ipkgd
	
	exit $?

elif [ $# -eq 5 -a "x$1" = "xintegcheck" -a "x$2" = "x-r" -a -d "$3" -a -f "$5" ] ; then
	
	root="$3"
	ipkname="$4"
	newipk=`realpath $5`

	rm -rf /tmp/ipkgd
	
	new=/tmp/ipkgd/new-ipk
	
	mkdir -p $new/c $new/d
	
	# unpack comparison archive	
	(cd $new ; ar x $newipk ; tar zxfC control.tar.gz c ; tar zxfC data.tar.gz d)
	
	(cd $new/c ; md5sum -b `find . -type f | sort` ) | sed 's/ \*\.\// *.\/'${ipkname}'./' > /tmp/ipkgd/new-control.md5
	(cd $root/usr/lib/ipkg/info ; find . -name "${ipkname}.*" | grep -v "${ipkname}.list" | sort | xargs -r md5sum -b ) > /tmp/ipkgd/orig-control.md5
	diff /tmp/ipkgd/orig-control.md5 /tmp/ipkgd/new-control.md5
	
	# TODO: verify control files

	# rm -rf /tmp/ipkgd
	
	exit $?


fi

usage
