#!/bin/bash
#
# Build Debian source packages. See dbuild(1) for more info.
# Lars Wirzenius <liw@iki.fi>
# 
# Licensed under the GPL.

set -e

fetchdir=source-files
keyring=/usr/share/keyrings/debian-keyring.pgp
rootcmd=fakeroot
username='NO USERNAME'
mailto=''
mail_maintainer=no
sign_changes=yes
check_signatures=yes
check_pgppass=no
check_configsite=no
verbose=no
verbose_logs=no
fix_for_frozen=no
clean_fetched=no
build_all=no
build_source=no
binopt=""
post_unpack_cmd=""
umask=`umask`

dbuildopts=""

# Write usage message
usage() {
	echo "usage: $progname [options] [filename-or-ftp-or-http-url ...]" 1>&2
	echo "       (more help on options at a later date)" 1>&2
}

# Write message to stdout if verbose messages are desired.
msg() {
	if [ "$verbose" = yes ]
	then
		echo "$@"
	fi
}

# Write error message to stderr and quit.
error() {
	echo "$@" 1>&2
	exit 1
}

# Mail stuff around: $1 is subject, $2 is file containing body, $3 and forward are
# recipient addresses.
mailto() {
	subject="$1"
	body="$2"
	shift 2
	addrs=""
	for addr in "$@"
	do
		if [ ! -z "$addr" ]
		then
			addrs="$addrs $addr"
		fi
	done
	if [ ! -z "$addrs" ]
	then
		mail -s "$subject" $addrs < $log
	fi
}

# Fetch one URL.
fetch() {
	msg "Fetching $1"
	mkdir -p $fetchdir
	(set -e; cd $fetchdir; wget --quiet --timestamping --retr-symlinks "$1")
}

# Fetch one URL that points at a directory.
fetch_dir() {
	msg "Fetching directory $1"
	mkdir -p $fetchdir
	(set -e; cd $fetchdir; \
	 wget --quiet --timestamping --retr-symlinks --recursive "$1")
}

# Check PGP signature on a file.
checkpgp() {
	msg "Checking PGP signature for $1"
	if pgp -f +batchmode +pubring=$keyring < "$1" 2>&1 >/dev/null |
	   grep "^Good signature from user" >/dev/null
	then
		return 0
	else
		return 1
	fi
}

# Extract names of other files to fetch from a .dsc.
parsedsc() {
	awk '/^Files: / { files=1; next }
	     files && /^ / && NF == 3 { print $3 }
	     files && !(/^ / && NF == 3) { files=0 }' "$1"
}

# Determine the proper name for the unpacked source directory from the .dsc name.
dsc2srcdir() {
	basename "$1" .dsc | sed 's/-[a-zA-Z0-9.+]*$//;s/_/-/'
}

# Build package from a .dsc.
build() {
	case "$1" in
	/*) dsc="$1" ;;
	*) dsc="`pwd`/$1" ;;
	esac

	$rootcmd /bin/rm -rf TEMP
	mkdir -p TEMP
	(
		cd TEMP
		dpkg-source -x "$dsc"
		
		dir=`dsc2srcdir "$dsc"`

		if [ "$fix_for_frozen" = yes ]
		then
			changes="$dir/debian/changelog"
			sed '1s/) unstable;/) frozen unstable;/' $changes > temp
			mv temp $changes
		fi

		if [ "$post_unpack_cmd" ]; then
			(
				cd $dir
                        	$post_unpack_cmd
			)
                fi

		(
			set -e
			cd $dir
			dpkg-buildpackage $rootaccess $binopt -m"$username" $signopts
		)
	) || return 1
	return 0
}


# The main program

# Fetch configuration
if [ -r "$HOME/.dbuildrc" ]
then
	. "$HOME/.dbuildrc"
fi

# Parse options
progname="$0"
loop=yes
while [ $loop = yes ]
do
	case "$1" in
	-u|--username)	username="$2"; dbuildopts="$dbuildopts -u $2"; shift 2 ;;
	-v|--verbose)	verbose=yes; dbuildopts="$dbuildopts -v"; shift ;;
	-V|--verbose-logs)	verbose_logs=yes; dbuildopts="$dbuildopts -V"; shift ;;
	-r|--root)	rootcmd="$2"; dbuildopts="$dbuildopts -r $2"; shift 2 ;;
	-m|--mail)	mailto="$2"; dbuildopts="$dbuildopts -m $2"; shift 2 ;;
	-k|--keyring)	keyring="$2"; dbuildopts="$dbuildopts -k $2"; shift 2 ;;
	-u|--umask) umask="$2"; dbuildopts="$dbuildopts -u $umask"; shift 2 ;;
	-f|--fix-for-frozen) fix_for_frozen=yes; dbuildopts="$dbuildopts -f"; shift ;;
	--mail-maintainer) mail_maintainer=yes; dbuildopts="$dbuildopts --mail-maintainer"; shift ;;
	--no-signature-checks) check_signatures=no; dbuildopts="$dbuildopts --no-signature-checks"; shift ;;
	-s|--no-signatures) sign_changes=no; dbuildopts="$dbuildopts -s"; shift ;;
	-p|--check-pgppass) check_pgppass=yes; dbuildopts="$dbuildopts -p"; shift ;;
	-c|--check-configsite) check_configsite=yes; dbuildopts="$dbuildopts -c"; shift ;;
	-C|--clean-fetched) clean_fetched=yes; dbuildopts="$dbuildopts -C"; shift ;;
	-a|--build-all) build_all=yes; dbuildopts="$dbuildopts -a"; shift ;;
	--build-source) build_source=yes; dbuildopts="$dbuildopts --build-source"; shift ;;
	--post-unpack-cmd) post_unpack_cmd="$2"; shift 2 ;;
	--)		shift; loop=no ;;
	-h|--help)	usage; exit 0 ;;
	-*)		error "unknown option $1" ;;
	*)		loop=no ;;
	esac
done

umask $umask

if [ -z "$rootcmd" ]
then
	rootaccess=""
else
	rootaccess="-r$rootcmd"
fi

if [ "$sign_changes" = yes ]
then
	signopts=""
else
	signopts="-us -uc"
fi

if [ "$build_all" = yes ]
then
	if [ "$build_source" = no ]
	then
		binopt="-b"
	fi
else
	binopt="-B"
fi

if [ "$check_pgppass" = yes ]
then
	if [ -z "$PGPPASS" ]
	then
		error "\$PGPPASS is empty!"
	fi

	# Snarfed and modified from debbuild by James Troup
        if ! echo "Oh my god, they killed Kenny" | pgp +batchmode -fast > /dev/null  2>&1
        then
          error "\$PGPPASS is incorrect"
        fi
fi

if [ "$check_configsite" = yes -a -z "$CONFIG_SITE" ]
then 
	error "\$CONFIG_SITE is not set."
fi

# Set up $build_arch
build_arch=`dpkg --print-architecture`

# Fetch and build
mkdir -p OK FAILED IGNORED WRONGARCH
for url in "$@"
do
	done=no
	
	msg "Processing $url"
	
	case "$url" in
	ftp://*.dsc|http://*.dsc)
		fetch "$url"
		fetched=yes
		dir=`echo "$url" | sed 's:/[^/]*$::'`
		base=`echo "$url" | sed 's:.*/::'`
		file="$fetchdir/$base"
		;;
	http://*/)
		error "No support for building whole directories via http, yet."
		;;
	ftp://*/)
		fetch_dir "$url"
		"$0" $dbuildopts source-files/.
		done=yes
		;;
	*.dsc)	
		fetched=no
		file="$url" 
		dir=`dirname "$file"`
		base=`basename "$file"`
		;;
	*)
		if [ ! -d "$url" ]
		then
			error "$url: Not a .dsc nor a directory"
		fi
		find "$url" -name '*.dsc' -print | 
		while read dsc2
		do
			"$0" $dbuildopts -- "$dsc2"
		done
		done=yes
		;;
	esac
	
	log="`basename $base .dsc`.messages"
	if [ ! -e OK/$base -a \
	     ! -e OK/$log -a \
	     ! -e FAILED/$base -a \
	     ! -e FAILED/$log -a \
	     ! -e IGNORED/$base -a \
	     ! -e IGNORED/$log -a \
	     ! "$done" = yes ]
	then
		rm -f $log
		echo "Automatic build of $base on $HOSTNAME by dbuild" > $log
		echo "Started at "$(date +%H:%M)" on "$(date +%Y-%m-%d) >> $log
		echo "*************************" >> $log

		package_arch=`grep "^Architecture: " $file | sed -e "s/Architecture: //"`
		case "$package_arch" in
		any|${build_arch}|${build_arch}\ *|*\ ${build_arch}|*\ ${build_arch}\ *) failed=no ;;
		all)
			case $build_all in
			yes) failed=no ;;
			*) failed=ignored
			echo "Package is for architecture all, but building of such packages was not" >> $log
			echo "requested by the user (option -a)." >> $log
			echo "Not building package." >> $log
			;;
			esac ;;
		*) echo "Package is for architecture $package_arch, not for $build_arch" >> $log
		   echo "Not building package." >> $log
		   failed=ignored;
		esac

		if [ "$failed" = ignored ]
		then
			mv $log WRONGARCH
			$rootcmd /bin/rm -rf TEMP
		elif [ "$failed" = yes ]
		then
			mv $log FAILED
			$rootcmd /bin/rm -rf TEMP
		else
			if [ "$fetched" = yes ]
			then
				otherfiles=`parsedsc "$file"`
				sourcefiles="$file"
				for otherfile in $otherfiles
				do
					fetch "$dir/$otherfile"
					sourcefiles="$sourcefiles $fetchdir/$otherfile"
				done
			else
				sourcefiles=""
			fi
	
			if [ "$mail_maintainer" = yes ]
			then
				maintainer=`sed -n '/^Maintainer: /s///p' $file`
			else
				maintainer=''
			fi
			
			result=0
			if [ "$check_signatures" = yes ]
			then
				if checkpgp "$file"
				then
					echo "PGP signature for $url is OK." >>$log
				else
					echo "ERROR! PGP SIGNATURE IS BAD FOR $url!" >>$log
					result=1
				fi
			else
				echo "PGP signature NOT checked, as requested." >>$log
			fi
				
			if [ "$result" = 0 ]
			then
				msg "Building $url"
				if build "$file" >>$log 2>&1 </dev/null
				then
					result=0
				else
					result=1
				fi
			fi
	
	
			echo "*************************" >> $log
			echo "Build finished at "$(date +%H:%M)" on "$(date +%Y-%m-%d) >> $log
	
			if [ $result = 0 ]
			then
				if [ "$verbose_logs" = yes ]
				then
					echo ''
					echo "*************************"
					echo "Changes file:"
					cat TEMP/*.changes
					echo ''
					echo "*************************"
					(
						set -e
						cd TEMP
						echo "Debs produced:" *.deb
						for deb in *.deb
						do
							echo ""
							echo "$deb:"
							dpkg -I $deb
						done
						for deb in *.deb
						do
							echo ""
							echo "$deb:"
							dpkg -c $deb
						done
					)
				fi >> $log
					
				mv $log TEMP/*.deb TEMP/*.changes OK
				for f in TEMP/*.nondebbin.tar.gz
				do
					if [ -e "$f" ]
					then
						mv $f OK
					fi
				done
				if [ "$build_source" = yes ]
				then
					mv TEMP/*.dsc OK
					case "$base" in
					*_*-*.dsc) 
						mv TEMP/*.diff.gz OK
						mv TEMP/*.orig.tar.gz OK
						;;
					*) 
						mv TEMP/*.tar.gz OK
						;;
					esac
				else
					$rootcmd /bin/rm -f TEMP/*.orig.tar.gz
				fi
				$rootcmd /bin/rm -f TEMP/*.orig.tar.gz
				$rootcmd /bin/rm -rf TEMP/`dsc2srcdir "$file"`
		
				# rmdir TEMP
				# Note: Not rm -rf (dir must be empty
				# by now)
				rm -rf TEMP
				# Note: fixing things so dbuild doesn't
				# crash when TEMP isn't empty requires too
				# much work right now. As a workaround,
				# let's ignore this problem for now.

				msg "$base built OK"
				mailto "Successful build log for $base" OK/$log $mailto $maintainer
			else
				echo "$url FAILED to build"
				echo "Build FAILED" >>$log
				mailto "Failed build log for $base" $log $mailto $maintainer
				mv $log FAILED
				$rootcmd /bin/rm -rf TEMP
			fi
		fi
		
		if [ "$fetched" = yes -a "$clean_fetched" = yes ]
		then
			msg "Removing $sourcefiles"
			rm -f $sourcefiles
		fi
	fi
done
