#!/bin/bash
# This script is run by rpmbuild at the end of the brp checks. It computes
# hashes of files listed in the BRP_PESIGN_FILES environment and stores them in
# %_topdir/OTHER/%name.cpio.rsasign. It also puts a specfile there, that
# is later used to repackage the RPMs.
#
# Copyright (c) 2013 SUSE Linux Products GmbH, Nuernberg, Germany.
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 2 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA

set -e

files="*.ko"
if test -n "${BRP_PESIGN_FILES+x}"; then
	files=${BRP_PESIGN_FILES}
fi
packages=
if test -n "${BRP_PESIGN_PACKAGES+x}"; then
	packages=${BRP_PESIGN_PACKAGES}
fi
output=
while test $# -gt 0; do
	case "$1" in
	--files)
		files=$2
		shift 2
		;;
	--packages)
		packages=$2
		shift 2
		;;
	--output)
		output=$2
		shift 2
		;;
	*)
		echo "$0: Unknown option: $1" >&2
		exit 1
	esac
done
if test -z "$files"; then
	exit 0
fi
if test -z "$output"; then
	output=`rpm --eval %_topdir/OTHER`
fi
if test -z "$RPM_BUILD_ROOT"; then
	echo "$0: warning: \$RPM_BUILD_ROOT not set, using the root directory" >&2
	RPM_BUILD_ROOT=/
fi

if ! mkdir -p "$output"; then
	echo "$0: warning: $output cannot be created, giving up" >&2
	exit 0
fi

# Handing RemovePathPostFixes
# Scanning for spec files and parsing package/subpackage names, RemovePathPostFixes
# Producing pkg_name:RemovePathPostFixes_suffix mapping string for later usage
SOURCE_DIR=$(rpm --eval '%{_sourcedir}')
SPEC_LIST=$(find "$SOURCE_DIR" -maxdepth 1 -name "*.spec" 2>/dev/null || :)
if [ -z "$SPEC_LIST" ]; then
    # If can not find any spec in _sourcedir, fallback here
    SPEC_LIST=$(find ../../../rpmbuild/SOURCES -maxdepth 1 -name "*.spec" 2>/dev/null || :)
fi
# pkg_name:rpp_suffix mapping string: "pkg-A:.suffixA pkg-B:.suffixB"
POSTFIX_MAP_STR=""
if [ -z "$SPEC_LIST" ]; then
    echo "No spec files found. RemovePaathPostFixes mapping failed."
else
    for spec in $SPEC_LIST; do
	# Using awk for parsing spec file
	# Name: main_pkg
	# %package: p_name
	# 	- without -n, p_name = main_pkg-$2
	# RemovePathPostfixes: p_suffix
	mappings=$(awk '{
            cmd = toupper($1)
            if (cmd == "NAME:") {
                main_pkg = $2
                current_pkg = main_pkg
            }
            else if (cmd == "%PACKAGE") {
                if ($2 == "-n") {
                    current_pkg = $3
                } else {
                    current_pkg = main_pkg "-" $2
                }
            }
            else if (cmd == "REMOVEPATHPOSTFIXES:") {
                print current_pkg ":" $2
            }
        }' "$spec")
	if [ -n "$mappings" ]; then
	    # attach new mapping entry to string
            POSTFIX_MAP_STR="$POSTFIX_MAP_STR $mappings"
        fi
    done
fi
# Remove leading and trailing whitespace
POSTFIX_MAP_STR=$(echo $POSTFIX_MAP_STR | xargs)
# Print postfix map string
if [ -n "$POSTFIX_MAP_STR" ]; then
    echo "Memory-based Postfix Map generated: $POSTFIX_MAP_STR"
fi

# Support RemovePathPostFixes, relocating files into Package-named folder. e.g.
# copy /usr/share/toto/systemd-bootx64.efi.standalone
# to /toto-standalone/usr/share/toto/systemd-bootx64.efi
# Then we will package the copy to RPM_PACKAGE_NAME.cpio.rsasign archive
if [ -n "$POSTFIX_MAP_STR" ]; then
    # Each entry in map string is pkg_name:p_suffix
    for entry in $POSTFIX_MAP_STR; do
	p_name="${entry%%:*}"
        p_suffix="${entry#*:}"
	# Search all files with p_suffix. e.g. .standalone
        find "$RPM_BUILD_ROOT" -type f -name "*$p_suffix" | while read -r src_file; do
	    # Remove the original p_suffix as new file name for copy
            # e.g. /.../systemd-bootx64.efi.standalone -> /.../systemd-bootx64.efi
            target_base_path="${src_file%$p_suffix}"
	    rel_path="${target_base_path#$RPM_BUILD_ROOT}"
	    # Add a new p_name folder behine buildroot
            # e.g. BUILD_ROOT/usr/share/toto/ -> BUILD_ROOT/toto-standalone/usr/share/toto/
	    target_file="$RPM_BUILD_ROOT/${p_name}${rel_path}"
	    target_dir=$(dirname "$target_file")
            # Run copy
            mkdir -p "$target_dir"
            cp -a "$src_file" "$target_file"
        done
    done
fi

case "$BRP_PESIGN_GRUB_RESERVATION" in
	'')
		pesign_grub_reservation="0"
		;;
	*[!0-9]*)
		echo "$0: warning: non-numerc value '$BRP_PESIGN_GRUB_RESERVATION' of BRP_PESIGN_GRUB_RESERVATION" >&2
		pesign_grub_reservation="0"
		;;
	*)
	pesign_grub_reservation="${BRP_PESIGN_GRUB_RESERVATION}"
	;;
esac

if test "${BRP_PESIGN_COMPRESS_MODULE}" = "xz"; then
	pesign_repackage_compress="--compress xz"
elif test "${BRP_PESIGN_COMPRESS_MODULE}" = "gzip"; then
	pesign_repackage_compress="--compress gzip"
elif test "${BRP_PESIGN_COMPRESS_MODULE}" = "zstd"; then
	pesign_repackage_compress="--compress zstd"
elif test "${BRP_PESIGN_COMPRESS_MODULE}" = "none"; then
	pesign_repackage_compress="--compress none"
elif test "${BRP_PESIGN_COMPRESS_MODULE}"; then
	echo "$0: error: unknown value of BRP_PESIGN_COMPRESS_MODULE" >&2
	exit 1
else
	pesign_repackage_compress=""
fi

cert=$RPM_SOURCE_DIR/_projectcert.crt
if test -e "$cert"; then
	echo "Using signing certificate $cert"
else
	echo "No buildservice signing certificate"
	cert=/dev/null
fi

pushd "$RPM_BUILD_ROOT"
# Producing the arguments of find command to pick files to .cpio.rsasign archive
args=()
for pattern in $files; do
	pattern=${pattern#/}
	if test "${pattern:0:2}" != "./"; then
		pattern="./$pattern"
	fi
	if test -d "$pattern"; then
		pattern="$pattern/*"
	fi
	# Support RemovePathPostFixes, check the pattern matches with any
	# suffix in Postfix Map file. If so, then we package the copy file
	# in /pkg_name to $RPM_PACKAGE_NAME.cpio.rsasign archive. e.g.
	# /toto-standalone/usr/share/toto/systemd-bootx64.efi
	# instead of
	# /usr/share/toto/systemd-bootx64.efi.standalone
        shifted_match=""
	if [ -n "$POSTFIX_MAP_STR" ]; then
	    for entry in $POSTFIX_MAP_STR; do
		p_name="${entry%%:*}"
		p_suffix="${entry#*:}"
		# If current pattern matches with suffix
		if [[ "$pattern" == *"$p_suffix" ]]; then
		    base_path="${pattern%$p_suffix}"
		    rel_base_path="${base_path#./}"
		    shifted_match="./${p_name}/${rel_base_path}"
                    break
		fi
	    done
	fi
        if [ -z "$shifted_match" ]; then
	    # Case A: no match, keep using original file pattern
            args=("${args[@]}" -o -path "$pattern")
        else
	    # Case B: match, using shifted match pattern
            args=("${args[@]}" -o -path "$shifted_match")
        fi
done
# delete the leading -o
unset args[0]

archive=$output/$RPM_PACKAGE_NAME.cpio.rsasign
archive_dir=$output/$RPM_PACKAGE_NAME
mkdir -p "$archive_dir"
# create an empty nss database to make pesign happy
nss_db=$(mktemp -d)
trap 'rm -rf "$nss_db"' EXIT
# strong password (in FIPS mode it is checked for strength)
echo 'Eir4;Qua.daeJ,hP0' > "$nss_db/passwd"
certutil -N -d "$nss_db" -f "$nss_db/passwd"

echo "Creating $archive"
files=($(find . -type f \( "${args[@]}" \)))
for f in "${files[@]}"; do
	dest="$archive_dir/$f"
	mkdir -p "${dest%/*}"
	case "$f" in
	./boot/* | *.efi | */lib/modules/*/vmlinu[xz] | */lib/modules/*/[Ii]mage | */lib/modules/*/z[Ii]mage)
		if [ -f /usr/bin/pesign ]; then
			pesign --certdir="$nss_db" -i "$f" -E $dest
		else
			# Non PE architectures like s390x
			cp "$f" "$dest"
		fi
		;;
	*)
		cp "$f" "$dest"
	esac
done
cd "$archive_dir"
nfiles="$(find . -type f | wc -l)"
if [ "$nfiles" != 0 ] ; then
	find . -type f | cpio -H newc -o >"$archive"
fi
rm -rf "$archive_dir"

# Removing temporary suffix package folders and its files before primary build
if [ -n "$POSTFIX_MAP_STR" ]; then
    for entry in $POSTFIX_MAP_STR; do
	p_name="${entry%%:*}"
	# Delete the $p_name/ folder located directly under the build root
	find "$RPM_BUILD_ROOT" -maxdepth 1 -type d -name "${p_name}" -exec rm -rf {} +
    done
fi

if [ "$nfiles" = 0 ] ; then
echo "No files in $archive, giving up"
	exit 0
fi
popd

# Show the content in .cpio.rsasign archive for debugging
if [ -f "$archive" ]; then
    cpio -it < "$archive" | sed 's/^/DEBUG: [CPIO] /'
fi

if test -e $RPM_SOURCE_DIR/pesign-spec-macros; then
	sed "
		s:%{name}:$RPM_PACKAGE_NAME:g
		s:%{version}:$RPM_PACKAGE_VERSION:g
	" $RPM_SOURCE_DIR/pesign-spec-macros > $output/pesign-spec-macros
	spec_macros="--macros pesign-spec-macros"
fi
if test -e $RPM_SOURCE_DIR/pesign-copy-sources; then
	sed "
		s:%{name}:$RPM_PACKAGE_NAME:g
		s:%{version}:$RPM_PACKAGE_VERSION:g
	" $RPM_SOURCE_DIR/pesign-copy-sources > $output/pesign-copy-sources
	while read -r line; do
		if [ -n "${line}" ]; then
			source_files="${source_files}${RPM_SOURCE_DIR}/${line}\n"
		fi
	done < $output/pesign-copy-sources
	echo -e "$source_files" | head -c -1 | cpio -o > $output/source_files.cpio
	rm $output/pesign-copy-sources
fi

# Collect all RemovePathPostFixes (RPP) pkg_names for sending to next stage
RPP_PACKAGES=""
if [ -n "$POSTFIX_MAP_STR" ]; then
    RPP_PACKAGES=$(echo "$POSTFIX_MAP_STR" | awk 'BEGIN{RS=" "; FS=":"} {print $1}' | xargs)
fi
echo "RPP_PACKAGES: $RPP_PACKAGES"

sed "
	s:@NAME@:$RPM_PACKAGE_NAME:g
	s:@PESIGN_GRUB_RESERVATION@:$pesign_grub_reservation:g
	s:@PESIGN_REPACKAGE_COMPRESS@:$pesign_repackage_compress:g
	s:@PESIGN_LOAD_SPEC_MACROS@:$spec_macros:g
	s:@PESIGN_PACKAGES@:$packages:g
	s:@RPP_PACKAGES@:$RPP_PACKAGES:g
	/@CERT@/ {
		r $cert
		d
	}
" /usr/lib/rpm/pesign/pesign-repackage.spec.in >"$output/pesign-repackage.spec"

date="$(LANG=C date --utc --date "@${SOURCE_DATE_EPOCH:-$(date +%s)}" '+%a %b %d %H:%M:%S %Z %Y')"
cat <<EOF >"$output/pesign-repackage.changes"
-------------------------------------------------------------------
$date - openSUSE <packaging@lists.opensuse.org>

- automatically generated

EOF

for rpmlintrc in $RPM_SOURCE_DIR/*rpmlintrc; do
	if test -e "$rpmlintrc"; then
		cp "$rpmlintrc" "$output/"
	fi
done
