<?php
/**
 * Onoxsoft SSO Plugin for Roundcube
 *
 * Roundcube'a single-sign-on token kabul eden plugin. ONOXSOFT panelinden
 * gelen `?_user=...&_token=...` parametrelerini doğrular ve oturumu açar.
 *
 * Token formatı: panel'in Cache deposunda 5dk TTL ile saklı:
 *   key:    onox:webmail-sso:{token}
 *   value:  {"email":"user@domain.com","password":"hashed-or-encrypted","expires_at":...}
 *
 * Plugin tüketim sonrası token'ı Cache'ten siler (single-use).
 *
 * Kurulum:
 *   1. /usr/share/roundcubemail/plugins/onox_sso/ altına kopyala
 *   2. config.inc.php içinde: $config['plugins'][] = 'onox_sso';
 *   3. config.inc.php'de panel database bilgisini set et: $config['onox_sso_redis_url']
 *
 * Author: ONOXSOFT
 * License: Proprietary
 * Version: 1.0
 */
class onox_sso extends rcube_plugin
{
    public $task = 'login|mail';
    public $noajax = true;
    public $noframe = true;

    function init()
    {
        $this->add_hook('startup', [$this, 'startup_hook']);
        $this->add_hook('authenticate', [$this, 'authenticate_hook']);
    }

    /**
     * URL'de _token varsa, panel'e bağlanıp doğrula.
     * Geçerli ise authenticate_hook ile session aç.
     */
    function startup_hook($args)
    {
        $rcmail = rcmail::get_instance();

        $sso_token = rcube_utils::get_input_value('_token', rcube_utils::INPUT_GET);
        $sso_user  = rcube_utils::get_input_value('_user',  rcube_utils::INPUT_GET);

        if (! $sso_token || ! $sso_user) {
            return $args;
        }

        // Daha önce auth olduysa direkt mail'e at
        if ($rcmail->user && $rcmail->user->ID && $rcmail->user->data['username'] === $sso_user) {
            $args['action'] = 'index';
            $args['task']   = 'mail';
            return $args;
        }

        // Cache'ten token doğrula (single-use, 5dk TTL)
        $payload = $this->consume_sso_token($sso_token);

        if (! $payload || $payload['email'] !== $sso_user) {
            rcube::raise_error([
                'code' => 500,
                'file' => __FILE__, 'line' => __LINE__,
                'message' => "Onox SSO: token doğrulanamadı veya süresi doldu (user={$sso_user})",
            ], true, false);
            return $args;
        }

        // Login akışına yönlendir — authenticate hook devralacak
        $this->sso_payload = $payload;
        $args['action'] = 'login';
        $args['task']   = 'login';

        $_POST['_user'] = $payload['email'];
        $_POST['_pass'] = $payload['password'];
        $_POST['_host'] = $payload['imap_host'] ?? 'localhost';
        $_POST['_action'] = 'login';

        return $args;
    }

    /**
     * Login sırasında ek doğrulama.
     */
    function authenticate_hook($args)
    {
        if (! empty($this->sso_payload) && $args['user'] === $this->sso_payload['email']) {
            $args['cookiecheck'] = false; // SSO'da CSRF cookie atlanabilir
        }
        return $args;
    }

    /**
     * Panel'in cache deposundan token oku ve sil (single-use).
     */
    private function consume_sso_token(string $token): ?array
    {
        $rcmail = rcmail::get_instance();
        $key = 'onox:webmail-sso:' . preg_replace('/[^a-f0-9]/', '', $token);

        // Token regex koruması — 32 hex karakter
        if (strlen(str_replace('onox:webmail-sso:', '', $key)) !== 32) {
            return null;
        }

        $redis_url = $rcmail->config->get('onox_sso_redis_url', 'tcp://127.0.0.1:6379');

        try {
            $redis = new Redis();
            // tcp://host:port[/db]
            if (preg_match('#^tcp://([^:/]+):(\d+)(?:/(\d+))?$#', $redis_url, $m)) {
                $redis->connect($m[1], (int)$m[2], 2.0);
                if (isset($m[3])) {
                    $redis->select((int)$m[3]);
                }
            } else {
                return null;
            }

            $value = $redis->get($key);
            if (! $value) {
                return null;
            }
            // Single-use: tüket
            $redis->del($key);
            $redis->close();

            $data = json_decode($value, true);
            if (! is_array($data) || empty($data['email']) || empty($data['password'])) {
                return null;
            }

            // TTL kontrolü (Cache zaten expire eder ama defense-in-depth)
            if (isset($data['expires_at']) && time() > (int)$data['expires_at']) {
                return null;
            }

            return $data;
        } catch (\Throwable $e) {
            rcube::raise_error([
                'code' => 500, 'file' => __FILE__, 'line' => __LINE__,
                'message' => 'Onox SSO Redis error: ' . $e->getMessage(),
            ], true, false);
            return null;
        }
    }

    private array $sso_payload = [];
}
