<?php
// =============================================================================
// /api/webview/qr.php?url=<url>
//
// Generates a QR-code PNG for the supplied URL. The app's `qr_url` field
// (returned in /auth) points here — the app appends the activation URL and
// fetches the PNG to display on the activation screen.
//
// No paid SDKs / php-gd-only:  we proxy through Google Chart-encoded
// api.qrserver.com (same approach the panel UI's 2FA enrollment uses).
// =============================================================================

$url = isset($_GET['url']) ? trim((string)$_GET['url']) : '';
$size = isset($_GET['size']) ? max(64, min(800, (int)$_GET['size'])) : 320;

if ($url === '') {
    header('Content-Type: text/plain; charset=utf-8', true, 400);
    echo "missing 'url' query parameter";
    exit;
}
if (strlen($url) > 2048) {
    header('Content-Type: text/plain; charset=utf-8', true, 400);
    echo "url too long";
    exit;
}

// addaplaylist.com integration: when this panel is registered with addaplaylist
// (config.addaplaylist_panel_code set), rewrite the QR payload from the legacy
// "<mac>|<device_key>" form to the addaplaylist activation URL so users scan it
// with their phone and complete pairing without the admin touching anything.
//
// The IBO app's QrLoader feeds us "url=|<device_key>" (the leading "|" is
// added by the app; suffix is the device_key shown on screen). Pull the trailing
// digits out and embed them as the activation code on addaplaylist.com.
//
// Unconfigured panels fall through unchanged → legacy QR renders exactly as
// today. There's no failure path that the legacy flow won't tolerate.
try {
    require_once __DIR__ . '/../../includes/db.php';
    $cfg_row = db_one('SELECT addaplaylist_panel_code FROM config WHERE id = 1');
    $aapl_code = trim((string)($cfg_row['addaplaylist_panel_code'] ?? ''));
    if ($aapl_code !== '' && preg_match('/^[A-Z0-9]{4,8}$/', $aapl_code)) {
        // Last contiguous numeric run in `url` is the device_key
        // (e.g. "|421374" → 421374, "|aa:fa:..|421374" → 421374).
        if (preg_match('/([0-9]{4,10})\s*$/', $url, $m)) {
            $device_code = $m[1];

            // addaplaylist requires the device_code to be pre-registered in
            // their Redis (TTL 120s) before /<panel_code>?code=<device_code>
            // resolves. Their /api/panel/device/init is open (no auth_token
            // — just needs a valid panel_code) and idempotent (overwrites
            // existing keys). Their device_id field requires ≥8 chars, so
            // we use the MAC stored against this device_key.
            $dev = db_one('SELECT mac_address FROM devices WHERE device_key = ?', [$device_code]);
            $mac = is_array($dev) ? (string)($dev['mac_address'] ?? '') : '';
            if ($mac !== '' && strlen($mac) >= 8) {
                $init_body = json_encode([
                    'panel_code'  => $aapl_code,
                    'device_code' => $device_code,
                    'device_id'   => $mac,
                ]);
                $ch_init = curl_init('https://addaplaylist.com/api/panel/device/init');
                curl_setopt_array($ch_init, [
                    CURLOPT_POST           => true,
                    CURLOPT_POSTFIELDS     => $init_body,
                    CURLOPT_HTTPHEADER     => ['Content-Type: application/json'],
                    CURLOPT_RETURNTRANSFER => true,
                    CURLOPT_CONNECTTIMEOUT => 3,
                    CURLOPT_TIMEOUT        => 6,
                ]);
                $init_resp = curl_exec($ch_init);
                $init_code = (int)curl_getinfo($ch_init, CURLINFO_HTTP_CODE);
                curl_close($ch_init);
                if ($init_code !== 200) {
                    error_log("qr.php: addaplaylist /device/init returned HTTP $init_code: " . substr((string)$init_resp, 0, 200));
                }
            } else {
                error_log("qr.php: no devices row for device_key={$device_code} — addaplaylist pre-register skipped");
            }

            $url = 'https://addaplaylist.com/' . $aapl_code . '?code=' . $device_code;
        }
    }
} catch (Throwable $e) {
    // Non-fatal — log to error_log and render the legacy URL so we never
    // regress an unconfigured panel because of an unrelated DB hiccup.
    error_log('qr.php: addaplaylist branch skipped: ' . $e->getMessage());
}

$qr_endpoint = 'https://api.qrserver.com/v1/create-qr-code/'
    . '?size=' . $size . 'x' . $size
    . '&margin=8'
    . '&format=png'
    . '&data=' . urlencode($url);

// Lock redirects to http(s) only so a compromised upstream can't bounce us
// to file:// / gopher:// / dict:// / ldap:// for SSRF amplification.
$proto_mask = (defined('CURLPROTO_HTTPS') ? CURLPROTO_HTTPS : 0)
            | (defined('CURLPROTO_HTTP')  ? CURLPROTO_HTTP  : 0);
$ch = curl_init($qr_endpoint);
curl_setopt_array($ch, [
    CURLOPT_RETURNTRANSFER  => true,
    CURLOPT_FOLLOWLOCATION  => true,
    CURLOPT_MAXREDIRS       => 3,
    CURLOPT_TIMEOUT         => 8,
    CURLOPT_USERAGENT       => 'Mozilla/5.0',
    CURLOPT_PROTOCOLS       => $proto_mask,
    CURLOPT_REDIR_PROTOCOLS => $proto_mask,
]);
$png  = curl_exec($ch);
$code = curl_getinfo($ch, CURLINFO_HTTP_CODE);
curl_close($ch);

if (!$png || $code !== 200) {
    header('Content-Type: text/plain; charset=utf-8', true, 502);
    echo "qr generator unreachable";
    exit;
}

header('Content-Type: image/png');
header('Cache-Control: public, max-age=86400');
echo $png;
