#!/bin/sh
PATH="/usr/bin:/bin:/usr/sbin:/sbin"

set -e

xslt_processor="xsltproc --nonet --novalid --maxdepth 25000"
#xslt_processor="xsltproc --nonet --novalid --maxdepth 25000 --profile"
tmpdir=`mktemp -d`
checker_dir="/usr/share/dbus-tools/policychecker"
schematron_dir="/usr/share/dbus-tools/policychecker/xslt"
conf_path=("/usr/share/dbus-1" "/etc/dbus-1")
sub_conf_path=("system.d" "session.d")
bus_type=-1
config_file=$1
schema_file="$checker_dir/rules.xsl"

system_privileges_file="$tmpdir/privileges_system"
conf_privileges_file="$tmpdir/privileges_conf"
cynara_db="/var/cynara/db"

cleanup() {
	rm -rf $tmpdir
	exit $1
}

trap cleanup 0

if [ "$#" -ne 1 ]; then
	echo "Usage: $0 config-file"
	exit 1
fi

if [ $config_file == "--system" ]; then
	bus_type=0
elif [ $config_file == "--session" ]; then
	bus_type=1
elif [ ! -f $config_file ]; then
	echo "config file does not exist"
	exit 1
fi

if [ ! -d $cynara_db ]; then
	echo "Cynara database not found"
	exit 1
fi

# Cynara privilege formats can be various.
# For example,
# SR: http://tizen.org/privilege/${privilege_name}
# DA: http://samsung.com/tizen/privilege/${privilege_name}
# IM: http://developer.samsung.com/tizen/privilege/${privilege_name}
# VD: http://developer.samsung.com/privilege/${privilege_name}
PRIVILEGES="
http://tizen.org/privilege
http://samsung.com/tizen/privilege
http://developer.samsung.com/tizen/privilege
http://developer.samsung.com/privilege
"

for p in $PRIVILEGES; do
	grep $p $cynara_db/* | cut -d\; -f3 | sort -u >> $system_privileges_file
done

function check_policy_file(){
	config_file="$1"

	echo "Checking D-Bus policy file: $config_file"

	# TEST 1/3: check cynara privileges existence (there are too many to perform this check using xsltproc)
	$xslt_processor $checker_dir/extract_privilege.xsl $config_file | sort -u > $conf_privileges_file
	grep -Fxv -f $system_privileges_file $conf_privileges_file | while read line ; do echo "FAILED(cynara) no privilege in cynara db: $line" ; exit 1; done

	# TEST 2/3: check allow/deny duplicates (impossible to do directly with xpath 1.0, I don't know how to embed it into schematron config)
	$xslt_processor $checker_dir/same.xsl $config_file

	# TEST 3/3: apply schematron rules

	# build a test (@user = x or @user = y or ...) at runtime
	prepare_test() {
		echo $(getent $1 | sort -r | awk -F: '{entries[n++] = $1} END { while (n>0) {printf "@'"$2"' = '\''%s'\''%s", entries[n-1], (n > 1 ? " or " : ""); n--} }')
	}

	users_test=$(prepare_test passwd user)
	groups_test=$(prepare_test group group)

	tmpname="$tmpdir/$(basename $schema_file)"

	cat $schema_file | sed -e "s/USERS_TEST/$users_test/g" -e "s/GROUPS_TEST/$groups_test/g" > $tmpname.0

	$xslt_processor $schematron_dir/iso_dsdl_include.xsl $tmpname.0 > $tmpname.1
	$xslt_processor $schematron_dir/iso_abstract_expand.xsl $tmpname.1 > $tmpname.2
	$xslt_processor $schematron_dir/iso_svrl_for_xslt1.xsl $tmpname.2 > $tmpname.3
	$xslt_processor $tmpname.3 $config_file > $tmpname.4
	$xslt_processor $checker_dir/report.xsl $tmpname.4

	# end-of-output, a new line for pretty printing
	echo
}

function check_policy_dir() {
	for d in "${conf_path[@]}"; do
		echo "$d/${sub_conf_path[$bus_type]}"
		target_path="$d/${sub_conf_path[$bus_type]}/*.conf"

		for f in $target_path; do
			if [ -f $f ]; then
				check_policy_file "$f"
			fi
		done
	done
}

if [ $bus_type -eq -1 ]; then
	check_policy_file "$config_file"
else
	check_policy_dir
fi

exit 0
