#!/bin/bash
# postinst script for #PACKAGE#
#
# see: dh_installdeb(1)

set -e

# Begin vars.sh
#!/bin/bash
# DO NOT EDIT
# This file is regenerated each run.
# If you want to add or change a variable, edit debify/settings.json and the vars hashref

source /etc/os-release
export OBS_REPO="x${NAME}_${VERSION_ID}"
if ls debian/*.install &> /dev/null; then
    perl -pi -e 's/\${env:OBS_REPO}/$ENV{OBS_REPO}/' debian/*.install
fi

export DEB_INSTALL_ROOT="/usr/src/packages/BUILD/debian/tmp"
export DEB_SOURCE_ROOT="/usr/src/packages/BUILD/debian/SOURCES_FROM_SPEC"
export RPM_SOURCE_DIR="/usr/src/packages/BUILD/debian/SOURCES_FROM_SPEC"
export __isa_bits="64"
export __isa_name="x86"
export __sourcedir=""
export _bindir="/opt/cpanel//root/usr/bin"
export _datadir="/opt/cpanel//root/usr/share"
export _defaultdocdir="/opt/cpanel//root/usr/share/doc"
export _docdir="/opt/cpanel//root/usr/share/doc"
export _exec_prefix="/opt/cpanel//root/usr"
export _includedir="/opt/cpanel//root/usr/include"
export _infodir="/opt/cpanel//root/usr/share/info"
export _isa=""
export _lib="lib64"
export _libdir="/opt/cpanel//root/usr/lib64"
export _libexecdir="/opt/cpanel//root/usr/libexec"
export _localstatedir="/opt/cpanel//root/var"
export _mandir="/opt/cpanel//root/usr/share/man"
export _prefix="/opt/cpanel//root/usr"
export _root_bindir="/opt/cpanel//root/usr/bin"
export _root_datadir="/opt/cpanel//root/usr/share"
export _root_exec_prefix="/opt/cpanel//root/usr"
export _root_includedir="/opt/cpanel//root/usr/include"
export _root_infodir="/opt/cpanel//root/usr/share/info"
export _root_initddir="/opt/cpanel//root/etc/rc.d/init.d"
export _root_libdir="/opt/cpanel//root/usr/lib64"
export _root_libexecdir="/opt/cpanel//root/usr/libexec"
export _root_localstatedir="/opt/cpanel//root/var"
export _root_mandir="/opt/cpanel//root/usr/share/man"
export _root_prefix="/opt/cpanel//root/usr"
export _root_sbindir="/opt/cpanel//root/usr/sbin"
export _root_sharedstatedir="/opt/cpanel//root/usr/com"
export _root_sysconfdir="/opt/cpanel//root/etc"
export _sbindir="/opt/cpanel//root/usr/sbin"
export _scl_prefix="/opt/cpanel"
export _scl_root="/opt/cpanel//root"
export _sysconfdir="/opt/cpanel//root/etc"
export _unitdir="/usr/lib/systemd/system"
export ai_advisor_version="1.35"
export buildroot="/usr/src/packages/BUILD"
export full_package_name="cpanel-ai-advisor-1.35"
export ix86="i386"
export name="cpanel-ai-advisor"
export nil="0"
export ns_name="ea"
export pkg=""
export pkg_name=""
export pkgname=""
export previous_version="1.34"
export release="1"
export release_prefix="1"
export scl=""
export scl_name=""
export scl_name_base=""
export scl_name_version=""
export scl_prefix=""
export version="1.35"
export version_with_debian_revision="1.35-1"
export SOURCE1="$DEB_SOURCE_ROOT/pkg.preinst"
export SOURCE2="$DEB_SOURCE_ROOT/pkg.postinst"
export SOURCE3="$DEB_SOURCE_ROOT/pkg.prerm"
export SOURCE4="$DEB_SOURCE_ROOT/pkg.postrm"
# End vars.sh

# summary of how this script can be called:
#        * <postinst> `configure' <most-recently-configured-version>
#        * <old-postinst> `abort-upgrade' <new version>
#        * <conflictor's-postinst> `abort-remove' `in-favour' <package>
#          <new-version>
#        * <postinst> `abort-remove'
#        * <deconfigured's-postinst> `abort-deconfigure' `in-favour'
#          <failed-install-package> <version> `removing'
#          <conflicting-package> <version>
# for details, see https://www.debian.org/doc/debian-policy/ or
# the debian-policy package


case "$1" in
    configure)
    ;;

    abort-upgrade|abort-remove|abort-deconfigure)
    ;;

    *)
        echo "postinst called with unknown argument \`$1'" >&2
        exit 1
    ;;
esac

# Contents of SOURCES/pkg.postinst
#!/bin/bash

# Create runtime directories.
mkdir -p /var/cpanel/ai/cache /var/cpanel/ai/proposals
# 0711 (traverse, not list): unprivileged cPanel users must open the 0644
# gateway.json directly (Cpanel::AI::Config::load reads it in-process during UAPI
# dispatch, no AdminBin) without being able to enumerate the dir, keeping
# server_token / .pkce_pending unlistable. The RPM also owns this mode via
# %dir %attr, but the deb build's dh_fixperms normalizes dirs to 0755 — so assert
# it here to keep RPM and deb consistent at 0711.
chmod 0711 /var/cpanel/ai

# ---------------------------------------------------------------------------
# Gateway configuration — create default if missing
# ---------------------------------------------------------------------------
GATEWAY_JSON="/var/cpanel/ai/gateway.json"

if [ ! -f "$GATEWAY_JSON" ]; then
    # Single gateway endpoint (assistant_url) -> the semantic-search-gate proxy,
    # which validates the WebPros OIDC JWT we send. auth_server is intentionally
    # omitted so the OAuth base URI comes from /var/cpanel/webpros_oidc_overrides
    # (staging) or the provider's built-in prod default.
    cat > "$GATEWAY_JSON" <<'JSON'
{
    "assistant_url": "https://semantic-search.plesk.com",
    "tls_verify": true
}
JSON
    chmod 0644 "$GATEWAY_JSON"
fi

# Telemetry salt (EP-CPAI-05 Story 5.7): a root-only 0600 secret, minted once at
# install (mirrors the server_token block above). Only root - via the ai_assistant
# AdminBin - reads it to derive CP_AI_* identity digests; the salt never enters an
# account process, so telemetry is safe on by default (sends stay consent-gated).
# No salt -> the emitter fails closed (off). It does not touch gateway.json; a
# legacy 5.1 hand-set distinct_id_salt there is unused by 5.7 and should be
# removed by hand.
TELEMETRY_SALT="/var/cpanel/ai/telemetry_salt"

# An existing salt must be a root-only 0600 file whose content is the canonical
# mint format (exactly 64 lowercase hex, no trailing newline). Mode drift (config
# management, a restore without -p) can expose the secret, and a weak/short value
# (e.g. a config-managed placeholder) yields brute-forceable HMACs - on either
# deviation, discard and re-mint rather than trust it (distinct_id rotation is
# acceptable for telemetry). The perl format check mirrors _read_salt exactly.
if [ -f "$TELEMETRY_SALT" ] && { [ "$(stat -c '%a %u' "$TELEMETRY_SALT" 2>/dev/null)" != "600 0" ] || \
     ! /usr/local/cpanel/3rdparty/bin/perl -e '
         open my $f, "<:raw", $ARGV[0] or exit 1;
         my $s = do { local $/; <$f> };
         exit( ( defined $s && $s =~ /\A[0-9a-f]{64}\z/ ) ? 0 : 1 );
     ' "$TELEMETRY_SALT"; }; then
    rm -f "$TELEMETRY_SALT"
    echo "cpanel-ai-advisor postinst: WARNING: telemetry salt was not a root-only 0600 64-hex secret - discarded, re-minting a fresh one." >&2
fi

if [ ! -f "$TELEMETRY_SALT" ]; then
    # Partial-write cleanup lives INSIDE the perl (unlink only after our own O_EXCL
    # create) so a concurrent create's EEXIST can never delete a salt this process
    # did not make.
    if /usr/local/cpanel/3rdparty/bin/perl -MFcntl=:DEFAULT -e '
        open my $u, "<:raw", "/dev/urandom" or die "urandom: $!";
        read( $u, my $b, 32 ) == 32 or die "short read";
        sysopen my $f, $ARGV[0], O_WRONLY | O_CREAT | O_EXCL, 0600 or die "create: $!";
        eval {
            print {$f} unpack( "H*", $b ) or die "write: $!";
            close $f or die "close: $!";
            1;
        } or do { unlink $ARGV[0]; die $@ };
    ' "$TELEMETRY_SALT"; then
        echo "cpanel-ai-advisor postinst: CP_AI_* telemetry salt minted (root-only 0600); telemetry on by default, still gated by user analytics consent (docs/DATA-PRIVACY.md section 3)." >&2
    else
        echo "cpanel-ai-advisor postinst: WARNING: telemetry salt minting failed - telemetry stays OFF (fail closed)." >&2
    fi
fi

# ---------------------------------------------------------------------------
# Symlink UAPI / SSE modules into cPanel's @INC path
# ---------------------------------------------------------------------------
# UAPI and SSE modules are resolved via Cpanel::LoadModule, which searches the
# ULC tree (/usr/local/cpanel) only, NOT /var/cpanel/perl. Symlink ours in so
# cpsrvd can find them.
ULC="/usr/local/cpanel"

# AIAssistant UAPI module (cPanel Meridian hub: AIAssistant::ask / ::status)
UAPI_AISRC="/var/cpanel/perl/Cpanel/API/AIAssistant.pm"
UAPI_AIDST="$ULC/Cpanel/API/AIAssistant.pm"

if [ -f "$UAPI_AISRC" ] && [ ! -e "$UAPI_AIDST" ]; then
    ln -sf "$UAPI_AISRC" "$UAPI_AIDST"
fi

# AIAssistant AdminBin module (privileged gateway.json read; shipped for
# AdminBin discovery — the v1 UAPI path uses the transport directly).
ADMIN_AISRC="/var/cpanel/perl/Cpanel/Admin/Modules/Cpanel/ai_assistant.pm"
ADMIN_AIDST="$ULC/Cpanel/Admin/Modules/Cpanel/ai_assistant.pm"

if [ -f "$ADMIN_AISRC" ] && [ ! -e "$ADMIN_AIDST" ]; then
    mkdir -p "$ULC/Cpanel/Admin/Modules/Cpanel"
    ln -sf "$ADMIN_AISRC" "$ADMIN_AIDST"
fi

# AIAssistant streaming SSE endpoint (cPanel Meridian: ./sse/AIAssistant).
# cpsrvd resolves SSE modules via Cpanel::LoadModule, which searches the ULC tree
# only (NOT /var/cpanel/perl), so symlink ours in - same pattern as the UAPI
# module above. NOTE: streaming also needs core cpsrvd support for delivering a
# POST body to SSE modules (Cpanel::Server::SSE::_get_request_body); that ships
# with cpanel core, not this package.
SSE_AISRC="/var/cpanel/perl/Cpanel/Server/SSE/cpanel/AIAssistant.pm"
SSE_AIDST="$ULC/Cpanel/Server/SSE/cpanel/AIAssistant.pm"

if [ -f "$SSE_AISRC" ] && [ ! -e "$SSE_AIDST" ]; then
    mkdir -p "$ULC/Cpanel/Server/SSE/cpanel"
    ln -sf "$SSE_AISRC" "$SSE_AIDST"
fi

# ---------------------------------------------------------------------------
# Register cPanel features (Feature Manager) — addonfeatures
# ---------------------------------------------------------------------------
# ai-assistant        : read tier. Gates the whole assistant + read actions.
# ai-assistant-actions: Act tier. Gates write actions.
#
# These MUST be registered as static addonfeatures descriptors: the Feature
# Manager edit path (WHMAPI update_featurelist) only accepts feature names it
# finds via load_feature_names()/load_addon_feature_names() — i.e. these files.
# A ConfigObj optional-component driver alone does NOT make the names valid for
# update_featurelist (it only feeds the create_featurelist/save path), so a
# component-only registration made both checkboxes silently un-saveable
# (CPANEL-54524 regressed this; the provider kill switch now lives entirely in
# Cpanel::AI::Config + the API layer, independent of Feature Manager visibility).
ADDON_DIR="/usr/local/cpanel/whostmgr/addonfeatures"
DEFAULT_FEATURES="/var/cpanel/features/default"

mkdir -p "$ADDON_DIR"

register_feature() {
    local token="$1"
    local default="$2"    # default feature-list value (1=on, 0=off)
    local src="/var/cpanel/ai/addonfeatures_${token}"
    local dst="$ADDON_DIR/${token}"

    if [ -f "$src" ] && [ ! -f "$dst" ]; then
        cp "$src" "$dst"
        chmod 0644 "$dst"
    fi

    # Set the feature in the default feature list if not already present. The Act
    # tier is written explicitly =0 so it is present-and-off (an admin can flip it
    # on in Feature Manager); runtime write capability is separately gated by the
    # fail-closed provider switch ai.enable_assistant_actions (Cpanel::AI::Config).
    if [ -f "$DEFAULT_FEATURES" ]; then
        if ! grep -q "^${token}=" "$DEFAULT_FEATURES" 2>/dev/null; then
            echo "${token}=${default}" >> "$DEFAULT_FEATURES"
        fi
    fi
}

register_feature "ai-assistant"         1
register_feature "ai-assistant-actions" 0

# Migration (idempotent): CPANEL-54524 briefly registered the assistant as a
# Cpanel::Component (config.json + ULC ConfigObj driver/META + Config.pm
# symlinks) INSTEAD of these addonfeatures, which made both checkboxes
# un-saveable via Feature Manager. Undo that registration on upgrade so no
# phantom component / dangling symlink is left pointing at the removed driver.
rm -f "$ULC/Cpanel/Config/ConfigObj/Driver/AiAssistant.pm"
rm -f "$ULC/Cpanel/Config/ConfigObj/Driver/AiAssistant/META.pm"
rmdir "$ULC/Cpanel/Config/ConfigObj/Driver/AiAssistant" 2>/dev/null || true
if [ -L "$ULC/Cpanel/AI/Config.pm" ]; then
    rm -f "$ULC/Cpanel/AI/Config.pm"
fi
PLUGINS_CONF="/var/cpanel/plugins/config.json"
if [ -s "$PLUGINS_CONF" ]; then
    /usr/local/cpanel/3rdparty/bin/perl -MCpanel::JSON -e '
        my $f = shift;
        my $d = eval { Cpanel::JSON::LoadFile($f) };    # leave untouched on parse failure
        if ( ref $d eq q{HASH} && exists $d->{q{ai-assistant}} ) {
            delete $d->{q{ai-assistant}};
            Cpanel::JSON::DumpFile( $f, $d );
        }
    ' "$PLUGINS_CONF" 2>/dev/null || true
fi

# ---------------------------------------------------------------------------
# Migration (idempotent): drop the retired AI Advisor frontend injections.
# ---------------------------------------------------------------------------
# Builds before 1.20 appended an AI_ADVISOR_PLUGIN marker block to the cPanel
# Jupiter content include and the WHM global banner. Those blocks load
# ai-advisor.css / ai-advisor.js from a frontend bundle this package no longer
# ships: that prototype UI was retired, and the surface we ship now is the
# Meridian hub, which loads its own assets. The matching cleanup used to live in
# pkg.prerm, which only runs on a full package removal - never on upgrade. So
# every server that ever carried a pre-1.20 build keeps emitting <link> and
# <script> tags for files that are gone, and each page load 404s twice.
#
# Strip only our own marker block, and only when the markers form a clean
# sequence of closed pairs. Checking that both markers merely EXIST is not
# enough: `/START/,/END/d` resolves the range end by scanning forward from the
# line after START, so any malformed layout can run the range past our block
# and delete unrelated customizations.
#
#   END before START  - the range opens at START, finds no END after it, and
#                       deletes to EOF.
#   START with no END - same runaway to EOF (the original guard's case).
#   START START ... END - the range closes at the first END and strands the
#                       second START, leaving a marker behind.
#   both on one line  - the end address is searched from the NEXT line, so the
#                       range runs on to the next END or EOF.
#
# A hand-edited or truncated file can be in any of these states, and this runs
# as root against files that may hold customizations this package does not own.
# Over-deleting is far worse than leaving a stale block behind, so on anything
# that is not well formed we touch nothing and say so loudly enough to be found
# in the update log. The include file itself is left in place even if it ends up
# empty - an empty include is inert.
_ai_advisor_markers_well_formed() {
    awk '
        /AI_ADVISOR_PLUGIN_START/ && /AI_ADVISOR_PLUGIN_END/ { bad = 1; exit }
        /AI_ADVISOR_PLUGIN_START/ { if (open) { bad = 1; exit } open = 1; seen = 1; next }
        /AI_ADVISOR_PLUGIN_END/   { if (!open) { bad = 1; exit } open = 0; next }
        END { exit( ( bad || !seen || open ) ? 1 : 0 ) }
    ' "$1" 2>/dev/null
}

for _stale_include in \
    /var/cpanel/customizations/content_includes/cpanel_jupiter_header_after.html.tt \
    /var/cpanel/customizations/whm/includes/global_banner.html.tt
do
    [ -f "$_stale_include" ] || continue
    grep -q 'AI_ADVISOR_PLUGIN' "$_stale_include" 2>/dev/null || continue

    if ! _ai_advisor_markers_well_formed "$_stale_include"; then
        echo "cpanel-ai-advisor postinst: WARNING: the AI_ADVISOR_PLUGIN markers in $_stale_include are malformed or out of order - leaving the file untouched. Remove the stale block by hand; deleting the marker range automatically could take unrelated customizations with it." >&2
        continue
    fi

    sed -i '/AI_ADVISOR_PLUGIN_START/,/AI_ADVISOR_PLUGIN_END/d' "$_stale_include"
    echo "cpanel-ai-advisor postinst: removed the retired AI Advisor asset injection from $_stale_include" >&2
done

# ---------------------------------------------------------------------------
# Reload cpsrvd so the freshly-installed/updated modules take effect.
# ---------------------------------------------------------------------------
# cpsrvd preloads our UAPI/SSE modules; a running daemon keeps serving the
# previously-loaded code until it is restarted. Without this, a fresh install or
# upgrade appears to "do nothing different" until the next unrelated cpsrvd
# restart. A graceful restart picks up the new code immediately.
if [ -x /usr/local/cpanel/scripts/restartsrv_cpsrvd ]; then
    /usr/local/cpanel/scripts/restartsrv_cpsrvd >/dev/null 2>&1 || true
fi

echo "AI Advisor plugin installed successfully"
# End SOURCES/pkg.postinst


exit 0
