#!/bin/sh
set -eu

umask 077
export LC_ALL=C

usage() {
    printf '%s\n' "Usage: install.sh --archive PACKAGE.tar.gz --signature PACKAGE.sig --public-key PUBLIC.pem [--manifest-url URL]" >&2
    exit 2
}

archive= signature= public_key= manifest_url=https://icq2.net/releases/unix/linux-x86_64/friends.manifest
while [ "$#" -gt 0 ]; do
    case "$1" in
        --archive) [ "$#" -ge 2 ] || usage; archive=$2; shift 2 ;;
        --signature) [ "$#" -ge 2 ] || usage; signature=$2; shift 2 ;;
        --public-key) [ "$#" -ge 2 ] || usage; public_key=$2; shift 2 ;;
        --manifest-url) [ "$#" -ge 2 ] || usage; manifest_url=$2; shift 2 ;;
        *) usage ;;
    esac
done
[ -f "$archive" ] && [ -f "$signature" ] && [ -f "$public_key" ] || usage
archive_size=$(wc -c <"$archive")
signature_size=$(wc -c <"$signature")
public_key_size=$(wc -c <"$public_key")
[ "$archive_size" -gt 0 ] && [ "$archive_size" -le 536870912 ] &&
    [ "$signature_size" -gt 0 ] && [ "$signature_size" -le 4096 ] &&
    [ "$public_key_size" -gt 0 ] && [ "$public_key_size" -le 16384 ] || {
        printf '%s\n' "The ICQ2 package or signature input exceeds safety limits." >&2
        exit 1
    }

case "$manifest_url" in
    https://icq2.net/*|https://www.icq2.net/*|https://download.icq2.net/*) ;;
    *) printf '%s\n' "The update discovery URL is not an approved ICQ2 HTTPS URL." >&2; exit 1 ;;
esac
case "$manifest_url" in *'?'*|*'#'*|*'@'*|*'\\'*) printf '%s\n' "The update discovery URL is invalid." >&2; exit 1 ;; esac

openssl pkeyutl -verify -pubin -inkey "$public_key" -rawin \
    -in "$archive" -sigfile "$signature" >/dev/null 2>&1 || {
        printf '%s\n' "The ICQ2 package signature is invalid." >&2
        exit 1
    }

[ "$(uname -s 2>/dev/null || true)" = Linux ] || {
    printf '%s\n' "This direct package supports Linux only." >&2
    exit 1
}
case "$(uname -m 2>/dev/null || true)" in
    x86_64|amd64) ;;
    *) printf '%s\n' "This package supports Linux x86-64 only." >&2; exit 1 ;;
esac

data_home=${XDG_DATA_HOME:-"$HOME/.local/share"}
cache_home=${XDG_CACHE_HOME:-"$HOME/.cache"}
app_root=${ICQ2_APP_ROOT:-"$data_home/icq2/client"}
bin_dir=${ICQ2_BIN_DIR:-"$HOME/.local/bin"}
install_cache="$cache_home/icq2/install"
mkdir -p "$app_root/versions" "$app_root/runtime" "$bin_dir" "$install_cache"
for command_path in "$bin_dir/icq2-tui" "$bin_dir/icq2-update"; do
    if [ -e "$command_path" ] && [ ! -L "$command_path" ]; then
        printf '%s\n' "Refusing to replace a non-link command path: $command_path" >&2
        exit 1
    fi
done
temporary_dir=$(mktemp -d "$install_cache/install.XXXXXX")
cleanup() {
    case "$temporary_dir" in "$install_cache"/install.*) rm -rf -- "$temporary_dir" ;; esac
}
trap cleanup EXIT HUP INT TERM

archive_root=$(tar -tzf "$archive" | sed -n '1{s:/*$::;s:/.*::;p;}')
printf '%s\n' "$archive_root" | grep -Eq '^icq2-[0-9]+\.[0-9]+\.[0-9][0-9]([ab])?-linux-x86_64$' || {
    printf '%s\n' "The ICQ2 package root is invalid." >&2
    exit 1
}
tar -tzf "$archive" | awk -v root="$archive_root" '
    BEGIN { ok=1; count=0 }
    { count++; entry=$0; sub(/\/$/,"",entry);
      if (entry=="" || substr(entry,1,1)=="/" || index(entry,"\\")!=0) ok=0;
      n=split(entry,p,"/"); if (p[1]!=root) ok=0;
      for(i=1;i<=n;i++) if(p[i]==".." || p[i]=="") ok=0 }
    END { exit !(ok && count>0) }
' || { printf '%s\n' "The ICQ2 package contains unsafe paths." >&2; exit 1; }
tar -tvzf "$archive" | awk '
    BEGIN { ok=1; total=0 }
    { if (NR>2048 || $3 !~ /^[0-9]+$/) ok=0; total+=$3; if(total>1073741824) ok=0 }
    END { exit !(ok && NR>0) }
' || { printf '%s\n' "The ICQ2 package expands beyond safety limits." >&2; exit 1; }
tar --no-same-owner --no-same-permissions -xzf "$archive" -C "$temporary_dir"
payload="$temporary_dir/$archive_root"
if find "$payload" -type l -print -quit | grep -q .; then
    printf '%s\n' "The ICQ2 package contains symbolic links." >&2
    exit 1
fi

metadata="$payload/package.env"
version=$(sed -n 's/^version=//p' "$metadata" | sed -n '1p')
[ "$archive_root" = "icq2-$version-linux-x86_64" ] || {
    printf '%s\n' "The ICQ2 package metadata does not match its name." >&2
    exit 1
}
[ "$(sed -n 's/^product=//p' "$metadata" | sed -n '1p')" = icq2-unix-tui ] || exit 1
[ "$(sed -n 's/^platform=//p' "$metadata" | sed -n '1p')" = linux ] || exit 1
[ "$(sed -n 's/^architecture=//p' "$metadata" | sed -n '1p')" = x86_64 ] || exit 1
[ -x "$payload/bin/icq-tui" ] && [ -x "$payload/runtime/icq2-update" ] &&
    [ -x "$payload/runtime/icq2-launcher" ] || {
        printf '%s\n' "The ICQ2 package is incomplete." >&2
        exit 1
    }
[ "$($payload/bin/icq-tui --version 2>/dev/null || true)" = "$version" ] || {
    printf '%s\n' "The ICQ2 executable version does not match the package." >&2
    exit 1
}

destination="$app_root/versions/$version"
[ ! -e "$destination" ] || {
    printf '%s\n' "ICQ2 $version is already installed." >&2
    exit 1
}
mv "$payload" "$destination"
cp "$public_key" "$destination/runtime/update-public-key.pem"
chmod 0644 "$destination/runtime/update-public-key.pem"
for file in icq2-launcher icq2-update; do
    cp "$destination/runtime/$file" "$app_root/runtime/$file.new.$$"
    chmod 0755 "$app_root/runtime/$file.new.$$"
    mv -f "$app_root/runtime/$file.new.$$" "$app_root/runtime/$file"
done
cp "$public_key" "$app_root/runtime/update-public-key.pem.new.$$"
chmod 0644 "$app_root/runtime/update-public-key.pem.new.$$"
mv -f "$app_root/runtime/update-public-key.pem.new.$$" "$app_root/runtime/update-public-key.pem"
printf 'manifest_url=%s\n' "$manifest_url" >"$app_root/update.conf.tmp.$$"
mv -f "$app_root/update.conf.tmp.$$" "$app_root/update.conf"
printf '%s\n' "$version" >"$app_root/staged-version"
"$app_root/runtime/icq2-update" --activate-staged

ln -s "$app_root/runtime/icq2-launcher" "$bin_dir/icq2-tui.new.$$"
mv -Tf "$bin_dir/icq2-tui.new.$$" "$bin_dir/icq2-tui"
ln -s "$app_root/runtime/icq2-update" "$bin_dir/icq2-update.new.$$"
mv -Tf "$bin_dir/icq2-update.new.$$" "$bin_dir/icq2-update"

printf '%s\n' "ICQ2 Unix TUI $version was installed for the current user."
printf '%s\n' "Run: $bin_dir/icq2-tui"
