#!/bin/sh -efu

. girar-sh-functions
. shell-quote
PROG='girar-task add'

usage()
{
	[ -z "$*" ] || message "$*"
	cat >&2 <<EOF
Usage: $PROG [<task_id>] repo <gear_repo> <gear_tag>
   or: $PROG [<task_id>] del <package>
   or: $PROG [<task_id>] copy <package> [<binary_repository_name>]
EOF
	exit 1
}

if [ "${1-}" = '--help' ]; then
	usage
fi

[ "$#" -le 4 ] || usage 'Too many arguments.'
[ "$#" -ge 2 ] || usage 'Not enough arguments.'

id=
case "$1" in
	[1-9]*)	id=$(PROG="$PROG" girar-task-find-current "$1"); shift ;;
	*)	id=$(PROG="$PROG" girar-task-find-current) ;;
esac

action="$1"; shift

enable -f /usr/lib/bash/lockf lockf

validate_repo_dir()
{
	local dir0="$dir"

	cd
	dir="$(printf %s "$dir" |tr -s /)"
	dir="$(prefix_packages "$dir")"
	dir="$(add_git_suffix "$dir")"
	[ "${dir#/}" != "$dir" ] ||
		dir="$GIRAR_HOME/$GIRAR_USER/$dir"
	[ "${dir#$GIRAR_HOME/}" != "$dir" ] &&
	printf '%s' "$dir" |egrep -qs "^$GIRAR_HOME/"'[a-z][a-z_0-9]+/packages/[A-Za-z0-9][-A-Za-z0-9_.]+[.]git$' ||
		fatal "$dir0: path to git repository does not belong to allowed directory tree"

	[ -d "$dir" ] ||
		fatal "$dir0: directory not available"

	cd "$dir"
	# obtain a shared lock on the source git repository.
	builtin lockf -s -v .
	dir="$PWD"
}

validate_tag_name()
{
	git rev-parse --symbolic --tags |fgrep -xqse "$tag_name" ||
		fatal "$tag_name: invalid tag name"
	tag_id="$(git rev-parse --verify "$tag_name")"
	if ! sig_text="$(GNUPGHOME=/usr/lib/alt-gpgkeys git verify-tag "$tag_id" 2>&1)" && ! sig_text="$(GNUPGHOME=/usr/lib/etersoft-gpgkeys git verify-tag "$tag_id" 2>&1)"; then
		printf >&2 '%s\n' "$sig_text"
		fatal "$tag_name: invalid tag"
	fi

	fpr="$(printf %s "$sig_text" |
		sed '/^gpg: Signature made .* using .* key ID */!d;s///;q')"
	[ -n "$fpr" ] ||
		fatal "$tag_name: gpg fingerprint not found"

	tag_author="$(GNUPGHOME=/usr/lib/alt-gpgkeys gpg --list-keys "$fpr" 2>/dev/null |
		sed '/^uid[[:space:]]\+/!d;s///;q')"
	[ -n "$tag_author" ] ||
	tag_author="$(GNUPGHOME=/usr/lib/etersoft-gpgkeys gpg --list-keys "$fpr" 2>/dev/null |
		sed '/^uid[[:space:]]\+/!d;s///;q')"
	[ -n "$tag_author" ] ||
		fatal "$tag_name: gpg uid not found"
	tag_author="$(printf %s "$tag_author" |
		LANG=C sed 's/@'"$(quote_sed_regexp "${EMAIL_DOMAIN%.*}")"'\.[a-z]\+>/@'"$(quote_sed_regexp "$EMAIL_DOMAIN")"'>/')"

	local userid
	userid="$(printf %s "$tag_author" |
		LANG=C sed -n 's/^[^<]\+<[[:space:]]*\([a-z][a-z0-9_-]\+\)\([[:space:]]*@\|[[:space:]]\+at[[:space:]]\+\).*$/\1/p' |
		tr '[:upper:]' '[:lower:]' |tr - _)"
	[ -n "$userid" -a -d "$GIRAR_PEOPLE_QUEUE/$userid" ] ||
		fatal "$tag_name: unacceptable signature: login name \`$userid' not found"
}

validate_srpm()
{
	cd "$HOME"

	[ -n "${srpm##*/*}" -a "$srpm" != "${srpm%.src.rpm}" ] ||
		fatal "$srpm: Invalid path"
	[ -f "$srpm" ] ||
		fatal "$srpm: File not found"
	package="$(rpmquery -p --qf '%{name}' "$srpm")" ||
		fatal "$srpm: rpmquery failed"

	cd - >/dev/null
}

case "$action" in
	repo)
	[ "$#" -eq 2 ] || usage 'Not enough arguments.'
	dir="$1"; shift
	validate_repo_dir
	tag_name="$1"; shift
	validate_tag_name
	;;
	del)
	[ "$#" -le 1 ] || usage 'Too many arguments.'
	[ "$#" -ge 1 ] || usage 'Not enough arguments.'
	package="$1"; shift
	;;
	copy)
	[ "$#" -ge 1 ] || usage 'Not enough arguments.'
	package="$1"; shift
	copy_repo=
	if [ $# -ge 1 ]; then
		copy_repo="$1"; shift
	fi
	[ -n "$copy_repo" ] || copy_repo=sisyphus
	copy_repo="$(printf %s "$copy_repo" |tr '[:upper:]' '[:lower:]')"
	;;
	srpm)
	[ "$#" -le 1 ] || usage 'Too many arguments.'
	[ "$#" -ge 1 ] || usage 'Not enough arguments.'
	srpm="$1"; shift
	validate_srpm
	;;
	*) usage "Invalid action: $action" ;;
esac

cd "$GB_TASKS/$id"
# obtain an exclusive lock on the TASKS structure
builtin lockf -n . ||
	fatal "task #$id is locked"

case "$action" in
	del|srpm)
	repo="$(cat task/repo)"
	girar-check-perms "$package" "$repo"
	;;
	copy)
	repo="$(cat task/repo)"
	[ "$repo" != "$copy_repo" ] ||
		fatal "Invalid request to copy package from $copy_repo to $repo"
	fgrep -ixqse "$copy_repo" "$GIRAR_REPOSITORIES" ||
		fatal "invalid repository \`$copy_repo', valid repositories are: $(fgrep -vixse "$repo" "$GIRAR_REPOSITORIES" |tr -s '[:space:]' ' ')"
	girar-check-perms "$package" "$repo"
	;;
esac

seq=
if [ -f task/seq ]; then
	seq=$(cat task/seq)
	case "$(($seq%3))" in
		# awaiting for build
		0) ;;
		# work in progress
		1) fatal "task #$id is a work in progress" ;;
		# have build results
		2)
		if [ 0 = "$(cat task/rc)" ]; then
			fatal "task #$id is already successfully processed"
		fi ;;
	esac
fi

nums=$(gear_nums)

check_already_added_repo()
{
	local i
	for i in $nums; do
		[ -s gears/$i/dir ] || return 0
		local a_dir a_tag_name a_tag_id
		a_dir=$(cat gears/$i/dir)
		a_tag_name=$(cat gears/$i/tag_name)
		a_tag_id=$(cat gears/$i/tag_id)
		if [ "${a_dir##*/}" = "${dir##*/}" ] &&
		   [ "$a_tag_name" = "$tag_name" ]; then
			message "${dir##*/}: tag $tag_name was already added"
			[ "$a_tag_id" = "$tag_id" ] ||
				fatal "${dir##*/}: tag $tag_name has changed"
			exit 0
		fi
		if [ "$a_tag_id" = "$tag_id" ]; then
			# maybe added by another guy
			message "${dir##*/}: tag $tag_name was already added (from $a_dir $a_tag_name)"
			exit 0
		fi
	done
}

check_already_added_package()
{
	local i
	for i in $nums; do
		[ -s gears/$i/package ] || continue
		local a_name a_copy
		a_name=$(cat gears/$i/package)
		[ "$a_name" = "$package" ] || continue
		a_copy="$(cat gears/$i/copy_repo 2>/dev/null ||:)"
		case "$action" in
			del)
			if [ -s gears/$i/copy_repo ]; then
				fatal "package $package was already queued by $(cat gears/$i/userid) for copy from $a_copy"
			else
				message "package $package was already queued by $(cat gears/$i/userid) for deletion"
				exit 0
			fi
			;;
			copy)
			if [ -s gears/$i/copy_repo ]; then
				message "package $package was already queued by $(cat gears/$i/userid) for copy from $a_copy"
				[ "$copy_repo" = "$a_copy" ] && exit 0
			else
				fatal "package $package was already queued by $(cat gears/$i/userid) for deletion"
			fi
			;;
		esac
	done
}

check_already_added()
{
	case "$action" in
		repo)
		check_already_added_repo
		;;
		del|copy)
		check_already_added_package
		;;
	esac
}

check_add_first()
{
	local task_owner
	task_owner=$(cat task/owner)
	[ "$GIRAR_USER" = "$task_owner" ] ||
		fatal "only $task_owner can add first subtask"
}

check_add_nth()
{
	[ -w gears ] ||
		fatal 'gears: Permission denied'
}

if [ -n "$nums" ]; then
	check_add_nth
	check_already_added
	i=$(printf %s "$nums" |tail -n1)
else
	check_add_first
	i=0
fi

i=$(($i+1))

atexit()
{
	local rc=$?
	trap - EXIT
	[ "$rc" -eq 0 ] || rm -rf gears/"$i"/ acl/"$i"/
	exit $rc
}

trap '' HUP INT QUIT PIPE TERM
# gears are not shared by default
mkdir -pm755 gears acl
mkdir -m2775 gears/"$i" acl/"$i"
trap atexit EXIT

case "$action" in
	del)
	printf '%s\n' "$package" >gears/$i/package
	printf '%s\n' "$GIRAR_USER" >gears/$i/userid
	echo >&2 "task #$id: added #$i: delete package $package from $repo"
	exit 0
	;;
	copy)
	printf '%s\n' "$copy_repo" >gears/$i/copy_repo
	printf '%s\n' "$package" >gears/$i/package
	printf '%s\n' "$GIRAR_USER" >gears/$i/userid
	echo >&2 "task #$id: added #$i: copy package $package from $copy_repo"
	exit 0
	;;
	srpm)
	mv -- "$HOME/$srpm" gears/$i/
	printf '%s\n' "$srpm" >gears/$i/srpm
	printf '%s\n' "$GIRAR_USER" >gears/$i/userid
	echo >&2 "task #$id: added #$i: build srpm $srpm"
	exit 0
	;;
esac

printf '%s\n' "$dir" >gears/$i/dir
printf '%s\n' "$tag_name" >gears/$i/tag_name
printf '%s\n' "$tag_id" >gears/$i/tag_id
printf '%s\n' "$tag_author" >gears/$i/tag_author
printf '%s\n' "$GIRAR_USER" >gears/$i/userid

mkdir gears/$i/git
GIT_DIR=gears/$i/git
export GIT_DIR
git init -q --template=/var/empty
git fetch -q "$dir" tag "$tag_name"
git branch master "tags/$tag_name"
touch gears/$i/git/git-daemon-export-ok

# directories added to alien task should be writable by task owner
[ -O gears ] ||
	find "gears/$i" -type d -execdir chmod g+w -- '{}' '+'

echo >&2 "task #$id: added #$i: build tag $tag_name from $dir"
