#!/bin/bash
#
# onox-license-check — Service-level license gate
#
# MODE: advisory (gozlem)
#   - exit 0 her zaman (servisi blok ETMEZ)
#   - lisansta yok ise log + crack-report (operatore bildirim)
#   - 1-2 hafta production gozlemi sonrasi ENFORCE_MODE=1 ile enforce'a gec
#
# MODE: enforce
#   - exit 1 lisansta yok ise (systemd start FAIL eder)
#   - ONOX_LICENSE_ENFORCE=1 environment ile aktive olur
#
# Usage:
#   onox-license-check <feature>
#
# Exit codes:
#   0 — feature licensed OR advisory mode (gec)
#   1 — feature NOT licensed (sadece enforce mode'da)
#   2 — system error (sadece enforce mode'da; advisory'de 0)
#
# Hedef kullanim:
#   /etc/systemd/system/<svc>.service.d/onox-license.conf:
#     [Service]
#     ExecStartPre=/usr/local/bin/onox-license-check mail_server
#
#   /opt/onoxsoft/bin/postfix (binary wrapper):
#     /usr/local/bin/onox-license-check mail_server || exit 1
#     exec /usr/sbin/postfix.real "$@"
#
# Security:
#   - Calls PHP LicenseManager::isFeatureLicensed() (JWT + tier whitelist AND-gate)
#   - 5sn timeout (systemd start hang olmasin)
#   - Fail-closed (enforce) / fail-open (advisory) — mode'a gore
#   - Logs: /var/log/onoxsoft/license-checks.log (60 gun rotate)
#   - Violations: /var/log/onoxsoft/license-violations.log (audit, kalici)

set -u

FEATURE="${1:-}"
UNIT="${2:-}"                         # v83.13: opsiyonel unit_name (postfix/exim) — MTA mismatch detect
ONOX_ROOT="${ONOX_ROOT:-/opt/onoxsoft}"
LOG_DIR="/var/log/onoxsoft"
LOG_FILE="$LOG_DIR/license-checks.log"
VIOLATION_LOG="$LOG_DIR/license-violations.log"
PHP_BIN="${PHP_BIN:-/usr/bin/php}"
ENFORCE_MODE="${ONOX_LICENSE_ENFORCE:-0}"

# Log dir
[ -d "$LOG_DIR" ] || mkdir -p "$LOG_DIR" 2>/dev/null
[ -w "$LOG_DIR" ] || { LOG_FILE="/dev/null"; VIOLATION_LOG="/dev/null"; }

# php_escape — bash helper, feature ismini PHP single-quoted literal'a cevirir.
# Whitelist: sadece [a-zA-Z0-9_]. Boylece SQL/PHP injection imkansiz.
# Tanim "violation"dan once olmali cunku violation cagriyor.
php_escape() {
    local clean
    clean="${1//[^a-zA-Z0-9_]/}"
    printf "'%s'" "$clean"
}

log() {
    local ts
    ts=$(date '+%Y-%m-%d %H:%M:%S')
    echo "[$ts] feature=$FEATURE pid=$$ mode=$ENFORCE_MODE status=$1 ${2:+detail=\"$2\"}" >> "$LOG_FILE" 2>/dev/null || true
}

# violation — kalici audit log + crack-report (background, fire-and-forget)
# Bash heredoc icine $(php_escape ...) ile pre-compute degerleri inject edilir.
# Boylece PHP -r icindeki kod TAMAMEN bash tarafindan duzgun parse edilir.
violation() {
    local ts caller reason="$1"
    ts=$(date '+%Y-%m-%d %H:%M:%S')
    caller=$(ps -o comm= -p "$PPID" 2>/dev/null || echo unknown)
    echo "[$ts] VIOLATION feature=$FEATURE caller_pid=$PPID caller=\"$caller\" reason=\"$reason\"" >> "$VIOLATION_LOG" 2>/dev/null || true

    # Pre-compute PHP literal degerleri (bash side)
    local feature_lit ppid_int enforce_lit host_lit
    feature_lit=$(php_escape "$FEATURE")
    ppid_int=$((PPID + 0))
    enforce_lit=$(php_escape "$ENFORCE_MODE")
    host_lit=$(php_escape "$(hostname 2>/dev/null || echo unknown)")

    # crack-report fire-and-forget (3sn timeout — systemd start bekletmesin)
    timeout 3 "$PHP_BIN" -d display_errors=0 -d log_errors=0 -r "
try {
    require '$ONOX_ROOT/vendor/autoload.php';
    \$app = require '$ONOX_ROOT/bootstrap/app.php';
    \$app->make(Illuminate\Contracts\Console\Kernel::class)->bootstrap();
    \$mgr = app(App\Domain\License\Services\LicenseManager::class);
    \$claims = \$mgr->getValidClaims();
    \$uuid = \$claims['sub'] ?? null;
    if (\$uuid) {
        \$client = app(App\Domain\License\Services\OnoxsoftLicenseClient::class);
        \$client->crackReport(\$uuid, 'service_unauthorized_start', [
            'feature'    => $feature_lit,
            'caller_pid' => $ppid_int,
            'mode'       => $enforce_lit,
            'host'       => $host_lit,
        ]);
    }
} catch (Throwable \$e) { /* sessiz — watchdog yine de devam etmeli */ }
" >/dev/null 2>&1 &
}

# Argument validation
if [ -z "$FEATURE" ]; then
    log "ERROR" "no_feature_argument"
    echo "onox-license-check: missing feature argument" >&2
    [ "$ENFORCE_MODE" = "1" ] && exit 2 || exit 0
fi

# Whitelist feature (anti-injection)
if ! [[ "$FEATURE" =~ ^[a-z_]+$ ]]; then
    log "ERROR" "invalid_feature_name"
    echo "onox-license-check: invalid feature name (alphanumeric+underscore only)" >&2
    [ "$ENFORCE_MODE" = "1" ] && exit 2 || exit 0
fi

# PHP/Laravel kurulum kontrolu
if [ ! -f "$ONOX_ROOT/vendor/autoload.php" ]; then
    log "ERROR" "no_laravel_at_$ONOX_ROOT"
    echo "onox-license-check: PANELTR installation not found at $ONOX_ROOT" >&2
    [ "$ENFORCE_MODE" = "1" ] && exit 2 || exit 0
fi

# ─── v83.13: MTA (Mail Transport Agent) mismatch detect ─────────────────────
# Onoxd watchdog veya systemd dependency Postfix'i tekrar start etmeye
# çalışırsa, aktif MTA driver Exim ise port 25 collision olur → Exim ölür.
# Bu pre-flight: feature=mail_server + unit_name belirtilmişse + DB'deki
# aktif MTA driver bu unit DEĞİLSE → exit 1 (servis BAŞLAMAZ — Exim hayatta kalır).
#
# Whitelist: sadece postfix|exim için MTA check (dovecot/rspamd/opendkim aktif MTA'dan bağımsız).
if [ "$FEATURE" = "mail_server" ] && [ -n "$UNIT" ]; then
    UNIT_CLEAN="${UNIT//[^a-z]/}"
    if [ "$UNIT_CLEAN" = "postfix" ] || [ "$UNIT_CLEAN" = "exim" ]; then
        # DB'den aktif MTA driver oku (2sn timeout — start hang'lemesin)
        ACTIVE_MTA=$(timeout 2 "$PHP_BIN" -d display_errors=0 -d log_errors=0 -r "
try {
    require '$ONOX_ROOT/vendor/autoload.php';
    \$app = require '$ONOX_ROOT/bootstrap/app.php';
    \$app->make(Illuminate\Contracts\Console\Kernel::class)->bootstrap();
    if (\Illuminate\Support\Facades\Schema::hasTable('server_settings')) {
        echo (string) \Illuminate\Support\Facades\DB::table('server_settings')
            ->where('key', 'mailserver_driver')->value('value');
    }
} catch (Throwable \$e) {}
" 2>/dev/null)

        # Aktif MTA bilinemiyorsa (DB down vb.) → tolerans, geç
        if [ -n "$ACTIVE_MTA" ] && [ "$ACTIVE_MTA" != "$UNIT_CLEAN" ]; then
            log "MTA_MISMATCH" "unit=$UNIT_CLEAN active=$ACTIVE_MTA — start blocked"
            echo "onox-license-check: MTA mismatch — '$UNIT_CLEAN' aktif değil (aktif: $ACTIVE_MTA). Servis başlatılmıyor." >&2
            # ADVISORY mode'da da BLOCK et (port 25 collision üretim downtime'ı)
            exit 1
        fi
    fi
fi

# Pre-compute PHP literal for feature (license check side)
FEATURE_LIT=$(php_escape "$FEATURE")

# License check (5sn timeout)
RESULT=$(timeout 5 "$PHP_BIN" -d display_errors=0 -d log_errors=0 -r "
try {
    require '$ONOX_ROOT/vendor/autoload.php';
    \$app = require '$ONOX_ROOT/bootstrap/app.php';
    \$app->make(Illuminate\Contracts\Console\Kernel::class)->bootstrap();
    \$mgr = app(App\Domain\License\Services\LicenseManager::class);
    exit(\$mgr->isFeatureLicensed($FEATURE_LIT) ? 0 : 1);
} catch (Throwable \$e) {
    fwrite(STDERR, 'license_check_exception: ' . \$e->getMessage() . PHP_EOL);
    exit(2);
}
" 2>&1)
EXIT=$?

case "$EXIT" in
    0)
        log "OK" "licensed"
        exit 0
        ;;
    1)
        # Lisansta YOK — violation kaydet + crack-report
        violation "feature_not_in_license_or_tier"

        if [ "$ENFORCE_MODE" = "1" ]; then
            log "DENIED" "enforce_mode_block"
            echo "onox-license-check: feature '$FEATURE' NOT in current license/tier (ENFORCED)" >&2
            exit 1
        else
            log "ADVISORY_DENY" "would_block_in_enforce_mode"
            echo "onox-license-check: feature '$FEATURE' NOT licensed (advisory only — not blocking)" >&2
            exit 0
        fi
        ;;
    124)
        log "TIMEOUT" "5s_exceeded"
        echo "onox-license-check: timeout (5s)" >&2
        [ "$ENFORCE_MODE" = "1" ] && exit 2 || exit 0
        ;;
    *)
        log "ERROR" "php_exception_or_unknown_$EXIT|$RESULT"
        echo "onox-license-check: error ($EXIT): $RESULT" >&2
        [ "$ENFORCE_MODE" = "1" ] && exit 2 || exit 0
        ;;
esac
