<?php
// =============================================================================
// POST /api/quick_url_encrypt.php — buyer-facing URL encryptor.
//
// Body: { action: 'encrypt', url: '<panel URL>' }
// Returns: { literal: '<ct_hex>|<iv_hex>' }
//
// The cert SHA + working key come from includes/apksec_constants.php (the
// values baked into the libapksec.so we ship to every buyer). There is no
// fallback — if the constants file is wrong, the build is broken and we want
// the buyer to know rather than silently emit garbage strings.
// =============================================================================
require_once __DIR__ . '/_common.php';
require_once __DIR__ . '/../includes/crypto.php';
require_csrf_unless_get();

list($action, $body) = read_action();

if ($action === 'verify') {
    // Diagnostic — decrypt a literal with THIS panel's constants and return
    // the resulting URL (or a reason if it's garbage). No state changes.
    $lit = trim((string)($body['literal'] ?? ''));
    if ($lit === '' || strpos($lit, '|') === false) {
        json_response(['ok' => false, 'reason' => 'Literal must look like ct_hex|iv_hex.']);
    }
    [$ct_hex, $iv_hex] = array_map('trim', explode('|', $lit, 2));
    if (!ctype_xdigit($ct_hex) || !ctype_xdigit($iv_hex)) {
        json_response(['ok' => false, 'reason' => 'Contains non-hex characters.']);
    }
    $ct = @hex2bin($ct_hex);
    $iv = @hex2bin($iv_hex);
    if ($ct === false || $iv === false || strlen($iv) !== 16 || strlen($ct) < 32) {
        json_response(['ok' => false, 'reason' => 'Bad hex length (ct=' . strlen((string)$ct) . ', iv=' . strlen((string)$iv) . ').']);
    }
    // Direct read from apksec_constants.php — same reasoning as the encrypt
    // path: verify must never trust the DB for crypto keys.
    $c = @include __DIR__ . '/../includes/apksec_constants.php';
    $cert = is_array($c) ? @hex2bin((string)($c['cert_sha256_hex']       ?? '')) : false;
    $wk   = is_array($c) ? @hex2bin((string)($c['target_server_key_hex'] ?? '')) : false;
    if ($cert === false || strlen($cert) !== 32 || $wk === false || strlen($wk) !== 16) {
        json_response(['ok' => false, 'reason' => 'Panel constants are misconfigured (check includes/apksec_constants.php).']);
    }
    $K      = hash('sha256', $cert . $wk . $iv, true);
    $cbc_iv = substr($ct, 0, 16);
    $cbody  = substr($ct, 16);   // reseller no-tail semantics
    $plain  = @openssl_decrypt($cbody, 'aes-256-cbc', $K, OPENSSL_RAW_DATA | OPENSSL_ZERO_PADDING, $cbc_iv);
    if ($plain === false || $plain === '') {
        json_response(['ok' => false, 'reason' => 'AES decrypt failed.']);
    }
    $pad = ord(substr($plain, -1));
    if ($pad < 1 || $pad > 16) {
        // Also try with-tail as a fallback so we can tell the operator which
        // format was used (customer/b3studios vs reseller).
        $cbody2 = substr($ct, 16, strlen($ct) - 48);
        if (strlen($cbody2) >= 16) {
            $plain2 = @openssl_decrypt($cbody2, 'aes-256-cbc', $K, OPENSSL_RAW_DATA | OPENSSL_ZERO_PADDING, $cbc_iv);
            $pad2 = $plain2 !== false ? ord(substr($plain2, -1)) : 0;
            if ($plain2 !== false && $pad2 >= 1 && $pad2 <= 16) {
                $u = substr($plain2, 0, -$pad2);
                if (preg_match('#^https?://#', $u)) {
                    json_response(['ok' => false, 'reason' => 'Literal is in the CUSTOMER format (with 32-byte tail). Buyer used a stale v11–v14 panel — re-encrypt on this reseller panel.', 'wrong_format_url' => $u]);
                }
            }
        }
        json_response(['ok' => false, 'reason' => 'Wrong keys — literal was generated by a panel with different apksec constants.']);
    }
    $url = substr($plain, 0, -$pad);
    if (!preg_match('#^https?://[[:print:]]{5,}#', $url)) {
        json_response(['ok' => false, 'reason' => 'Decrypts to non-URL bytes.']);
    }
    json_response(['ok' => true, 'url' => $url]);
}

if ($action !== 'encrypt') {
    json_error('Unknown action.', 400);
}

$url = trim((string)($body['url'] ?? ''));
if ($url === '') {
    json_error('Please enter a URL.', 400);
}
if (strlen($url) > 256) {
    json_error('URL is too long (max 256 characters).', 400);
}
if (!preg_match('#^https?://[^\s]{3,}$#i', $url)) {
    json_error('URL must start with http:// or https://.', 400);
}

// Enforce a trailing slash before encrypting. The app concatenates the
// decrypted panel URL with raw endpoint names — GlassVpnPicker uses
// `_panelUrl + "vpns.php"` (no leading slash on the endpoint side), so a
// missing trailing slash silently produces `.../slugvpns.php` → 404 →
// "Could not load VPN list" and similar. /auth and /has_playlist happen
// to work either way because they're routed through a smarter wrapper.
// Force the slash so the buyer can't paste a URL that breaks half the app.
if (substr($url, -1) !== '/') {
    $url .= '/';
}

// Read crypto constants DIRECTLY from apksec_constants.php — bypass db_config()
// entirely so a broken DB (readonly, missing config row, seeded with bad
// values) can't produce a broken literal. The file bundled with the reseller
// zip has the correct hex-encoded ASCII values matching libapksec_reseller.so.
$constants_path = __DIR__ . '/../includes/apksec_constants.php';
$constants = is_file($constants_path) ? @include $constants_path : null;
if (!is_array($constants)) {
    json_error(
        'includes/apksec_constants.php is missing or unreadable. '
      . 'Make sure that file shipped intact with your reseller bundle.',
        500
    );
}
$cert_hex = (string)($constants['cert_sha256_hex']       ?? '');
$wk_hex   = (string)($constants['target_server_key_hex'] ?? '');

if ($cert_hex === '' || $wk_hex === '') {
    json_error(
        "Crypto constants aren't configured on this panel. "
      . "includes/apksec_constants.php exists but cert_sha256_hex / target_server_key_hex are empty.",
        500
    );
}

try {
    $r = ns_encrypt_url($cert_hex, $wk_hex, $url);
} catch (Throwable $e) {
    panel_log_exception($e, ['endpoint' => '/api/quick_url_encrypt.php', 'url' => $url]);
    json_error('Couldn\'t encrypt the URL. Check the panel error log for details.', 500);
}

json_response(['literal' => $r['literal']]);
