#!/bin/bash
# ----------------------------------------------------------------
# Copyright (c) uib gmbh (www.uib.de)
# This sourcecode is owned by uib
# and published under the Terms of the General Public License.
# ----------------------------------------------------------------
#
# Post installation script
#	This script executes after unpacking files from the archive and registering the product at the server
#
# The following environment variables can be used to obtain information about the current installation:
#	CLIENT_DATA_DIR: directory where client data will be installed
#	DEPOT_ID: id of the depot
#	PACKAGE_VERSION: package version
#	PRODUCT_ID: id of the current product
#	PRODUCT_TYPE: type of the current product
#	PRODUCT_VERSION: product version

echo -e "Setting variables"
	TMP_DIR=${CLIENT_DATA_DIR}/../${PRODUCT_ID}.tmp

echo -e "\nChecking if $TMP_DIR exists"
	if [ -d $TMP_DIR ]; then
		echo -e "\e[0;32m\tTemporary directory $TMP_DIR exist\e[0m"
	else
		echo -e "\e[0;31m\tTemporary directory $TMP_DIR does not exist. Aborting!\e[0m" 1>&2
		exit 1
	fi

echo -e "\nChecking for directories that need to be restored"
	for dirname in custom files/preload installfiles* localsetup/custom localsetup/installfiles* opsi; do
		for path in $TMP_DIR/$dirname; do
			echo -e "\tChecking if $path exists"
			if [ -e $path ]; then
				echo -e "\e[0;32m\t\t$path exists\n\e[0m"
				echo -e "\tChecking if $path is a symlink or a directory"
				if [ -L $path ]; then
					echo -e "\e[0;32m\t\t$path is a symlink\e[0m"
					if [[ $path == *"/files/"* ]]; then
						echo -e "\e[0;32m\t\tRecreating $path in $CLIENT_DATA_DIR/files/\e[0m\n"
							test -e $CLIENT_DATA_DIR/files/`basename $path` && rm -rf $CLIENT_DATA_DIR/files/`basename $path`
							ln -sr "$(readlink -f "$path")" "$CLIENT_DATA_DIR/files/`basename $path`"
					elif [[ $path == *"/localsetup/"* ]]; then
						echo -e "\e[0;32m\t\tRecreating $path in $CLIENT_DATA_DIR/localsetup/\e[0m\n"
							test -e $CLIENT_DATA_DIR/localsetup/`basename $path` && rm -rf $CLIENT_DATA_DIR/localsetup/`basename $path`
							ln -sr "$(readlink -f "$path")" "$CLIENT_DATA_DIR/localsetup/`basename $path`"
					else
						echo -e "\e[0;32m\t\tRecreating $path in $CLIENT_DATA_DIR/\e[0m\n"
							test -e $CLIENT_DATA_DIR/`basename $path` && rm -rf $CLIENT_DATA_DIR/`basename $path`
							ln -sr "$(readlink -f "$path")" "$CLIENT_DATA_DIR/`basename $path`"
					fi
				elif [ -d $path ]; then
					echo -e "\e[0;32m\t\t$path is a directory\e[0m"
					if [[ $path == *"/files/"* ]]; then
						echo -e "\e[0;32m\t\tMoving directory $path to $CLIENT_DATA_DIR/files\e[0m\n"
							test -e $CLIENT_DATA_DIR/files/`basename $path` && rm -rf $CLIENT_DATA_DIR/files/`basename $path`
							mv $path $CLIENT_DATA_DIR/files || exit 1
					elif [[ $path == *"/localsetup/"* ]]; then
						echo -e "\e[0;32m\t\tMoving directory $path to $CLIENT_DATA_DIR/localsetup\e[0m\n"
							test -e $CLIENT_DATA_DIR/localsetup/`basename $path` && rm -rf $CLIENT_DATA_DIR/localsetup/`basename $path`
							mv $path $CLIENT_DATA_DIR/localsetup || exit 1
					else
						echo -e "\e[0;32m\t\tMoving directory $path to $CLIENT_DATA_DIR\e[0m\n"
							test -e $CLIENT_DATA_DIR/`basename $path` && rm -rf $CLIENT_DATA_DIR/`basename $path`
							mv $path $CLIENT_DATA_DIR || exit 1
					fi
				fi
			else
				echo -e "\e[0;31m\t\t$path does not exist\e[0m\n"
			fi
		done
	done

echo "Checking if multiple installfiles directories are present"
	ADDITIONAL_INSTALLFILES_DIR=`ls -d $CLIENT_DATA_DIR/localsetup/installfiles*`
	if [ -n "$ADDITIONAL_INSTALLFILES_DIR" ]; then
		echo "Determining the former default value of the installfiles_dir property"
			default_value=`opsi-cli --output-format json jsonrpc execute productPropertyState_getValues windows11-upgrade installfiles_dir $DEPOT_ID | cut -d'[' -f2 | cut -d'"' -f2`
			property_id="installfiles_dir"
			possible_values=""

		for dirname in $CLIENT_DATA_DIR/localsetup/installfiles*; do
			dirname=`basename $dirname`
			if [ "$default_value" = "" ]; then
				echo "Setting first found dirname as default if default_value is empty"
					default_value="$dirname"
			fi

			if [ -n "$possible_values" ]; then
				echo "Separating entries of possible_values by comma and whitespace if possible_values is not empty"
					possible_values="${possible_values}, "
			fi

			echo "Adding $dirname to $possible_values"
				possible_values=${possible_values}\"${dirname}\"
		done

		echo "Creating product property installfiles_dir with all found installfiles dirs as selectable values"
			create_property="opsi-admin -d method productProperty_createUnicode $PRODUCT_ID \"$PRODUCT_VERSION\" \"$PACKAGE_VERSION\" $property_id 'installfiles dir to use as installation source' '[${possible_values}]' '[${default_value}]' true false"
			$create_property

		echo "Setting the default value for the property"
			opsi-admin -d method productPropertyState_create $PRODUCT_ID $property_id $DEPOT_ID ${default_value}
	else
		echo "No additional installfiles directories found"
	fi

echo "Generating the .files file with symlink support"
	set +e
	# opsiconfd runs this script from a temp copy, so $(dirname $0) is NOT the
	# real OPSI/ directory generatepcf actually lives in. $SCRIPT_PATH (set by
	# opsiconfd to the real on-disk path of this script) gives the right directory.
	SCRIPT_DIR=$(dirname "$SCRIPT_PATH")
	if [ $(uname -m) == 'x86_64' ]; then
		# 64-bit
		chmod +x "$SCRIPT_DIR/generatepcf"
		"$SCRIPT_DIR/generatepcf" -p $PRODUCT_ID -d $CLIENT_DATA_DIR
	else
		# 32-bit
		chmod +x "$SCRIPT_DIR/generatepcf-x86"
		"$SCRIPT_DIR/generatepcf-x86" -p $PRODUCT_ID -d $CLIENT_DATA_DIR
	fi
	set -e

echo -e "\nCleaning up stale product property states for properties no longer declared by this package"
	# Compares the properties this package version actually declares against the
	# property IDs that currently have stored ProductPropertyStates for this
	# product. Anything in the states but not in the declarations is orphaned -
	# usually because a later package removed the property - and is deleted.
	# Deliberately not a hardcoded list: whichever property gets removed in a
	# future version, its leftover states are cleaned up automatically here.
	echo -e "\tFetching properties declared by $PRODUCT_ID $PRODUCT_VERSION-$PACKAGE_VERSION"
		declared_properties_file=$(mktemp)
		opsi-cli --output-format json jsonrpc execute productProperty_getObjects > "$declared_properties_file"

	echo -e "\tFetching properties that currently have stored states for $PRODUCT_ID"
		stored_properties_file=$(mktemp)
		opsi-cli --output-format json jsonrpc execute productPropertyState_getObjects > "$stored_properties_file"

	# python3 instead of jq: python3 is required by opsiconfd itself, so it is
	# guaranteed to be present on any server that can run this script at all.
	# jq is a separate tool that may not be installed on a given depot server.
	stale_properties=$(python3 -c '
import json, sys
pid, pv, pkv, declared_file, stored_file = sys.argv[1:6]
with open(declared_file) as f:
    declared = json.load(f)
with open(stored_file) as f:
    stored = json.load(f)
declared_ids = {p["propertyId"] for p in declared if p.get("productId") == pid and p.get("productVersion") == pv and p.get("packageVersion") == pkv}
stored_ids = {p["propertyId"] for p in stored if p.get("productId") == pid}
print(json.dumps(sorted(stored_ids - declared_ids)))
' "$PRODUCT_ID" "$PRODUCT_VERSION" "$PACKAGE_VERSION" "$declared_properties_file" "$stored_properties_file")
	rm -f "$declared_properties_file" "$stored_properties_file"

	stale_count=$(python3 -c "import json,sys; print(len(json.loads(sys.argv[1])))" "$stale_properties")

	if [ "$stale_count" -gt 0 ]; then
		stale_list_display=$(python3 -c "import json,sys; print(\", \".join(json.loads(sys.argv[1])))" "$stale_properties")
		echo -e "\tFound $stale_count propert(y/ies) with stored states no longer declared by this package: $stale_list_display"
		for property_id in $(python3 -c "import json,sys; print(chr(10).join(json.loads(sys.argv[1])))" "$stale_properties"); do
			echo -e "\t\tDeleting all stored states for stale property '$property_id'"
				opsi-cli jsonrpc execute productPropertyState_delete "$PRODUCT_ID" "$property_id" "*" >/dev/null
		done
	else
		echo -e "\tNo stale property states found"
	fi

echo -e "Removing temporary directory"
	rm -rf $TMP_DIR
	exit 0