PL
games/xencenter-xenadmin-xcp-jumphp / src / Form / AddServerForm.php · commit fb1b7c9
<?phpnamespace App\Form;use App\Model\LocalNetwork;use App\Model\ServerProfile;use JumPHP\Gui\Button;use JumPHP\Gui\CheckBox;use JumPHP\Gui\ComboBox;use JumPHP\Gui\Container;use JumPHP\Gui\Form;use JumPHP\Gui\Label;use JumPHP\Gui\TextBox;use JumPHP\Kernel\Platform;use JumPHP\Native\FilePicker;use JumPHP\Native\Native;/** * The Add New Server dialog: the server, how to reach it (HTTP, HTTPS on the desktop, an SSH tunnel), the account. * Closes with ['profile' => ServerProfile]: MainForm adds it and connects. * * @property TextBox $host * @property ComboBox $method * @property Label $portLabel * @property TextBox $port * @property TextBox $user * @property TextBox $password * @property Container $sshPanel * @property TextBox $sshUser * @property TextBox $sshPort * @property Button $chooseKey * @property Label $keyState * @property CheckBox $remember * @property Label $hint * @property Container $localPanel * @property Label $localWarning * @property Button $localFix * @property Label $error * @property Button $demo * @property Button $add * @property Button $cancel */final class AddServerForm extends Form{    public function __construct(private \App\Model\Session $session)    {        parent::__construct();    }    /** XAPI's answers, events and the console go on while this dialog is open. */    protected function tick(float $dt): void    {        $this->session->tick($dt);        $this->showLocalProblem();    }    /**     * Browser + a local address (192.168.x.x, xcp.lan...) + the local network off: the JumPHP server cannot reach it, the runtime is     * required. Shown under the fields as soon as the address is typed, with the button that fixes it. Returns the problem.     */    public function showLocalProblem(): ?string    {        $host = (string)preg_replace('#^https?://#', '', rtrim(trim($this->host->text), '/'));        $problem = LocalNetwork::wasm() ? LocalNetwork::check($host, $this->method()) : null;        $this->localPanel->visible = $problem !== null;        if ($problem !== null) {            $this->localWarning->text = $problem;            $this->localFix->text = LocalNetwork::buttonText($this->engine->runtime()->state());        }        return $problem;    }    public function onLocalFixClick(mixed $sender = null): void    {        LocalNetwork::fix();        $this->showLocalProblem();    }    /** @var list<string> the methods in the order of the list */    private array $methods = [];    private string $key = '';    protected function loaded(array $params = []): void    {        $this->methods = array_values(array_filter(['http', 'https', 'ssh'], fn ($m) => $m !== 'https' || !Platform::isWasm()));        $this->method->items = array_map(fn ($m) => ServerProfile::METHODS[$m], $this->methods);        $this->method->selected = 0;        $this->key = '';        $this->error->text = '';        $this->localPanel->visible = false;        $this->host->text = (string)($params['host'] ?? '');        $this->onMethodChange($this->method, 0, '');        $this->setFocus($this->host);    }    public function method(): string    {        return $this->methods[(int)$this->method->selected] ?? 'http';    }    public function onMethodChange(ComboBox $sender, int $index, string $item): void    {        $method = $this->method();        $this->sshPanel->visible = $method === 'ssh';        $this->portLabel->text = $method === 'ssh' ? 'XAPI port:' : 'Port:';        $this->port->placeholder = $method === 'https' ? '443' : '80';        $this->hint->text = match ($method) {            'http' => Platform::isWasm()                ? 'In a browser the connection leaves from your own network when the JumPHP runtime runs on this computer, else through the JumPHP server (public addresses only: a local address such as 192.168.x.x needs the runtime).'                : 'Plain HTTP on port 80: the password crosses the network unencrypted. Prefer HTTPS or the SSH tunnel outside a trusted network.',            'https' => 'HTTPS on port 443. The server\'s certificate is accepted as XenCenter does on a first connection (XCP-ng servers have self-signed ones).',            default => Platform::isWasm() ? 'OpenSSH runs in your browser (WebAssembly): the key never leaves it.' : 'The ssh program of this computer is used (your ~/.ssh and agent work too).',        };    }    public function onHostSubmit(TextBox $sender, string $text): void    {        $this->setFocus($this->user->text === '' ? $this->user : $this->password);    }    public function onChooseKeyClick(Button $sender): void    {        if (!Native::supports('open')) {            $this->keyState->text = 'No file chooser here: use the password.';            return;        }        FilePicker::open(['title' => 'SSH private key'])->then(function (?string $file): void {            if ($file === null) {                return;            }            $text = (string)@file_get_contents($file);            if (Platform::isWasm()) {                @unlink($file);            }            if (!str_contains($text, 'PRIVATE KEY')) {                $this->keyState->text = 'This is not a private key.';                return;            }            $this->key = Platform::isWasm() ? $text : $file;            $this->keyState->text = 'Key: ' . basename($file);        });    }    public function onDemoClick(Button $sender): void    {        $this->close(['demo' => true]);    }    public function onAddClick(mixed $sender = null): void    {        $host = trim($this->host->text);        if ($host === '' || preg_match('/\s/', $host)) {            $this->error->text = 'Enter the address of the server.';            $this->setFocus($this->host);            return;        }        $host = (string)preg_replace('#^https?://#', '', rtrim($host, '/'));        $port = trim($this->port->text);        if ($port !== '' && (!ctype_digit($port) || (int)$port < 1 || (int)$port > 65535)) {            $this->error->text = 'The port is a number from 1 to 65535.';            return;        }        if ($this->showLocalProblem() !== null) {            $this->error->text = 'This server is on your local network: the JumPHP runtime is required (see above).';            return;        }        if ($this->user->text === '') {            $this->error->text = 'Enter a user name.';            return;        }        $this->close(['profile' => new ServerProfile($host, trim($this->user->text), $this->password->text, $this->method(), (int)$port, $this->remember->checked,            trim($this->sshUser->text) ?: 'root', (int)($this->sshPort->text ?: 22), $this->key)]);    }    public function onCancelClick(Button $sender): void    {        $this->close();    }}