Created a new unified DAV endpoint
This commit is contained in:
parent
49ba29f4da
commit
a36d05971a
6 changed files with 325 additions and 130 deletions
168
Core/Frameworks/Baikal/Core/Server.php
Normal file
168
Core/Frameworks/Baikal/Core/Server.php
Normal file
|
@ -0,0 +1,168 @@
|
|||
<?php
|
||||
#################################################################
|
||||
# Copyright notice
|
||||
#
|
||||
# (c) 2013 Jérôme Schneider <mail@jeromeschneider.fr>
|
||||
# All rights reserved
|
||||
#
|
||||
# http://baikal-server.com
|
||||
#
|
||||
# This script is part of the Baïkal Server project. The Baïkal
|
||||
# Server project is free software; you can redistribute it
|
||||
# and/or modify it under the terms of the GNU General Public
|
||||
# License as published by the Free Software Foundation; either
|
||||
# version 2 of the License, or (at your option) any later version.
|
||||
#
|
||||
# The GNU General Public License can be found at
|
||||
# http://www.gnu.org/copyleft/gpl.html.
|
||||
#
|
||||
# This script is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# This copyright notice MUST APPEAR in all copies of the script!
|
||||
#################################################################
|
||||
|
||||
namespace Baikal\Core;
|
||||
|
||||
use PDO;
|
||||
|
||||
/**
|
||||
* The Baikal Server
|
||||
*
|
||||
* This class sets up the underlying Sabre\DAV\Server object.
|
||||
*
|
||||
* @copyright Copyright (C) Jérôme Schneider <mail@jeromeschneider.fr>
|
||||
* @author Evert Pot (http://evertpot.com/)
|
||||
* @license http://sabre.io/license/ GPLv2
|
||||
*/
|
||||
class Server {
|
||||
|
||||
/**
|
||||
* Is CalDAV enabled?
|
||||
*
|
||||
* @var bool
|
||||
*/
|
||||
protected $enableCalDAV;
|
||||
|
||||
/**
|
||||
* is CardDAV enabled?
|
||||
*
|
||||
* @var bool
|
||||
*/
|
||||
protected $enableCardDAV;
|
||||
|
||||
/**
|
||||
* "Basic" or "Digest"
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $authType;
|
||||
|
||||
/**
|
||||
* HTTP authentication realm
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $authRealm;
|
||||
|
||||
/**
|
||||
* Reference to Database object
|
||||
*
|
||||
* @var PDO
|
||||
*/
|
||||
protected $pdo;
|
||||
|
||||
/**
|
||||
* baseUri for the sabre/dav server
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $baseUri;
|
||||
|
||||
/**
|
||||
* The sabre/dav Server object
|
||||
*
|
||||
* @var \Sabre\DAV\Server
|
||||
*/
|
||||
protected $server;
|
||||
|
||||
|
||||
/**
|
||||
* Creates the server object.
|
||||
*
|
||||
* @param bool $enableCalDAV
|
||||
* @param bool $enableCardDAV
|
||||
* @param string $authType
|
||||
* @param string $authRealm
|
||||
* @param PDO $pdo
|
||||
* @param string $baseUri
|
||||
*/
|
||||
function __construct($enableCalDAV, $enableCardDAV, $authType, $authRealm, PDO $pdo, $baseUri) {
|
||||
|
||||
$this->enableCalDAV = $enableCalDAV;
|
||||
$this->enableCardDAV = $enableCardDAV;
|
||||
$this->authType = $authType;
|
||||
$this->authRealm = $authRealm;
|
||||
$this->pdo = $pdo;
|
||||
$this->baseUri = $baseUri;
|
||||
|
||||
$this->initServer();
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Starts processing
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
function start() {
|
||||
|
||||
$this->server->exec();
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Initializes the server object
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
protected function initServer() {
|
||||
|
||||
if ($this->authType === 'Basic') {
|
||||
$authBackend = new \Baikal\Core\PDOBasicAuth($this->pdo, $this->authRealm);
|
||||
} else {
|
||||
$authBackend = new \Sabre\DAV\Auth\Backend\PDO($this->pdo, $this->authRealm);
|
||||
}
|
||||
$principalBackend = new \Sabre\DAVACL\PrincipalBackend\PDO($this->pdo);
|
||||
|
||||
$nodes = [
|
||||
new \Sabre\CalDAV\Principal\Collection($principalBackend)
|
||||
];
|
||||
if ($this->enableCalDAV) {
|
||||
$calendarBackend = new \Sabre\CalDAV\Backend\PDO($this->pdo);
|
||||
$nodes[] = new \Sabre\CalDAV\CalendarRoot($principalBackend, $calendarBackend);
|
||||
}
|
||||
if ($this->enableCardDAV) {
|
||||
$carddavBackend = new \Sabre\CardDAV\Backend\PDO($GLOBALS["DB"]->getPDO());
|
||||
$nodes[] = new \Sabre\CardDAV\AddressBookRoot($principalBackend, $carddavBackend);
|
||||
}
|
||||
|
||||
$this->server = new \Sabre\DAV\Server($nodes);
|
||||
$this->server->setBaseUri($this->baseUri);
|
||||
|
||||
$this->server->addPlugin(new \Sabre\DAV\Auth\Plugin($authBackend, $this->authRealm));
|
||||
$this->server->addPlugin(new \Sabre\DAVACL\Plugin());
|
||||
$this->server->addPlugin(new \Sabre\DAV\Browser\Plugin());
|
||||
|
||||
if ($this->enableCalDAV) {
|
||||
$this->server->addPlugin(new \Sabre\CalDAV\Plugin());
|
||||
}
|
||||
if ($this->enableCardDAV) {
|
||||
$this->server->addPlugin(new \Sabre\CardDAV\Plugin());
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
|
@ -45,6 +45,10 @@ class System extends \Baikal\Model\Config {
|
|||
"type" => "litteral",
|
||||
"comment" => 'Should begin and end with a "/"',
|
||||
),
|
||||
"BAIKAL_DAV_BASEURI" => array(
|
||||
"type" => "litteral",
|
||||
"comment" => 'Should begin and end with a "/"',
|
||||
),
|
||||
"PROJECT_SQLITE_FILE" => array(
|
||||
"type" => "litteral",
|
||||
"comment" => "Define path to Baïkal Database SQLite file",
|
||||
|
@ -85,6 +89,7 @@ class System extends \Baikal\Model\Config {
|
|||
"BAIKAL_AUTH_REALM" => "BaikalDAV",
|
||||
"BAIKAL_CARD_BASEURI" => 'PROJECT_BASEURI . "card.php/"',
|
||||
"BAIKAL_CAL_BASEURI" => 'PROJECT_BASEURI . "cal.php/"',
|
||||
"BAIKAL_DAV_BASEURI" => 'PROJECT_BASEURI . "dav.php/"',
|
||||
"PROJECT_SQLITE_FILE" => 'PROJECT_PATH_SPECIFIC . "db/db.sqlite"',
|
||||
"PROJECT_DB_MYSQL" => FALSE,
|
||||
"PROJECT_DB_MYSQL_HOST" => "",
|
||||
|
@ -119,6 +124,16 @@ class System extends \Baikal\Model\Config {
|
|||
"content" => "If Baïkal is hosted in a subfolder, this path should reflect it.<br /><strong>Whatever happens, it should begin and end with a slash.</strong>"
|
||||
)
|
||||
)));
|
||||
$oMorpho->add(new \Formal\Element\Text(array(
|
||||
"prop" => "BAIKAL_DAV_BASEURI",
|
||||
"label" => "CalDAV/CardDAV base URI",
|
||||
"validation" => "required",
|
||||
"help" => "The absolute web path to dav.php",
|
||||
"popover" => array(
|
||||
"title" => "DAV base URI",
|
||||
"content" => "If Baïkal is hosted in a subfolder, this path should reflect it.<br /><strong>Whatever happens, it should begin and end with a slash.</strong>"
|
||||
)
|
||||
)));
|
||||
|
||||
$oMorpho->add(new \Formal\Element\Text(array(
|
||||
"prop" => "BAIKAL_AUTH_REALM",
|
||||
|
@ -213,6 +228,9 @@ define("BAIKAL_CARD_BASEURI", PROJECT_BASEURI . "card.php/");
|
|||
# Should begin and end with a "/"
|
||||
define("BAIKAL_CAL_BASEURI", PROJECT_BASEURI . "cal.php/");
|
||||
|
||||
# Should begin and end with a "/"
|
||||
define("BAIKAL_DAV_BASEURI", PROJECT_BASEURI . "dav.php/");
|
||||
|
||||
# Define path to Baïkal Database SQLite file
|
||||
define("PROJECT_SQLITE_FILE", PROJECT_PATH_SPECIFIC . "db/db.sqlite");
|
||||
|
||||
|
@ -241,4 +259,4 @@ CODE;
|
|||
$sCode = trim($sCode);
|
||||
return $sCode;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,87 +0,0 @@
|
|||
<?php
|
||||
/***************************************************************
|
||||
* Copyright notice
|
||||
*
|
||||
* (c) 2013 Jérôme Schneider <mail@jeromeschneider.fr>
|
||||
* All rights reserved
|
||||
*
|
||||
* http://baikal-server.com
|
||||
*
|
||||
* This script is part of the Baïkal Server project. The Baïkal
|
||||
* Server project is free software; you can redistribute it
|
||||
* and/or modify it under the terms of the GNU General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2 of the License, or (at your option) any later version.
|
||||
*
|
||||
* The GNU General Public License can be found at
|
||||
* http://www.gnu.org/copyleft/gpl.html.
|
||||
*
|
||||
* This script is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* This copyright notice MUST APPEAR in all copies of the script!
|
||||
***************************************************************/
|
||||
|
||||
define("BAIKAL_CONTEXT", TRUE);
|
||||
define("PROJECT_CONTEXT_BASEURI", "/");
|
||||
|
||||
if(file_exists(getcwd() . "/Core")) {
|
||||
# Flat FTP mode
|
||||
define("PROJECT_PATH_ROOT", getcwd() . "/"); #./
|
||||
} else {
|
||||
# Dedicated server mode
|
||||
define("PROJECT_PATH_ROOT", dirname(getcwd()) . "/"); #../
|
||||
}
|
||||
|
||||
if(!file_exists(PROJECT_PATH_ROOT . 'vendor/')) {
|
||||
die('<h1>Incomplete installation</h1><p>Baïkal dependencies have not been installed. Please, execute "<strong>composer install</strong>" in the folder where you installed Baïkal.');
|
||||
}
|
||||
|
||||
require PROJECT_PATH_ROOT . 'vendor/autoload.php';
|
||||
|
||||
# Bootstraping Flake
|
||||
\Flake\Framework::bootstrap();
|
||||
|
||||
# Bootstrapping Baïkal
|
||||
\Baikal\Framework::bootstrap();
|
||||
|
||||
if(!defined("BAIKAL_CARD_ENABLED") || BAIKAL_CARD_ENABLED !== TRUE) {
|
||||
throw new ErrorException("Baikal CardDAV is disabled.", 0, 255, __FILE__, __LINE__);
|
||||
}
|
||||
|
||||
# Backends
|
||||
if(
|
||||
BAIKAL_DAV_AUTH_TYPE == "Basic" || (
|
||||
array_key_exists('HTTP_USER_AGENT', $_SERVER) && (
|
||||
preg_match('/Windows-Phone-WebDAV-Client/i', $_SERVER['HTTP_USER_AGENT']) ||
|
||||
preg_match('/MSFT-WP\/8.10.*/i', $_SERVER['HTTP_USER_AGENT'])
|
||||
)
|
||||
)
|
||||
) {
|
||||
$authBackend = new \Baikal\Core\PDOBasicAuth($GLOBALS["DB"]->getPDO(), BAIKAL_AUTH_REALM);
|
||||
} else {
|
||||
$authBackend = new \Sabre\DAV\Auth\Backend\PDO($GLOBALS["DB"]->getPDO());
|
||||
}
|
||||
|
||||
$principalBackend = new \Sabre\DAVACL\PrincipalBackend\PDO($GLOBALS["DB"]->getPDO());
|
||||
$carddavBackend = new \Sabre\CardDAV\Backend\PDO($GLOBALS["DB"]->getPDO());
|
||||
|
||||
# Setting up the directory tree
|
||||
$nodes = array(
|
||||
new \Sabre\DAVACL\PrincipalCollection($principalBackend),
|
||||
new \Sabre\CardDAV\AddressBookRoot($principalBackend, $carddavBackend),
|
||||
);
|
||||
|
||||
# The object tree needs in turn to be passed to the server class
|
||||
$server = new \Sabre\DAV\Server($nodes);
|
||||
$server->setBaseUri(BAIKAL_CARD_BASEURI);
|
||||
|
||||
# Plugins
|
||||
$server->addPlugin(new \Sabre\DAV\Auth\Plugin($authBackend, BAIKAL_AUTH_REALM));
|
||||
$server->addPlugin(new \Sabre\CardDAV\Plugin());
|
||||
$server->addPlugin(new \Sabre\DAVACL\Plugin());
|
||||
|
||||
# And off we go!
|
||||
$server->exec();
|
|
@ -1 +0,0 @@
|
|||
../Core/Frameworks/Baikal/WWWRoot/cal.php
|
65
html/cal.php
Normal file
65
html/cal.php
Normal file
|
@ -0,0 +1,65 @@
|
|||
<?php
|
||||
/***************************************************************
|
||||
* Copyright notice
|
||||
*
|
||||
* (c) 2013 Jérôme Schneider <mail@jeromeschneider.fr>
|
||||
* All rights reserved
|
||||
*
|
||||
* http://baikal-server.com
|
||||
*
|
||||
* This script is part of the Baïkal Server project. The Baïkal
|
||||
* Server project is free software; you can redistribute it
|
||||
* and/or modify it under the terms of the GNU General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2 of the License, or (at your option) any later version.
|
||||
*
|
||||
* The GNU General Public License can be found at
|
||||
* http://www.gnu.org/copyleft/gpl.html.
|
||||
*
|
||||
* This script is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* This copyright notice MUST APPEAR in all copies of the script!
|
||||
***************************************************************/
|
||||
|
||||
ini_set("session.cookie_httponly", 1);
|
||||
ini_set("display_errors", 0);
|
||||
ini_set("log_errors", 1);
|
||||
|
||||
define("BAIKAL_CONTEXT", TRUE);
|
||||
define("PROJECT_CONTEXT_BASEURI", "/");
|
||||
|
||||
if(file_exists(getcwd() . "/Core")) {
|
||||
# Flat FTP mode
|
||||
define("PROJECT_PATH_ROOT", getcwd() . "/"); #./
|
||||
} else {
|
||||
# Dedicated server mode
|
||||
define("PROJECT_PATH_ROOT", dirname(getcwd()) . "/"); #../
|
||||
}
|
||||
|
||||
if(!file_exists(PROJECT_PATH_ROOT . 'vendor/')) {
|
||||
die('<h1>Incomplete installation</h1><p>Baïkal dependencies have not been installed. Please, execute "<strong>composer install</strong>" in the folder where you installed Baïkal.');
|
||||
}
|
||||
|
||||
require PROJECT_PATH_ROOT . 'vendor/autoload.php';
|
||||
|
||||
# Bootstraping Flake
|
||||
\Flake\Framework::bootstrap();
|
||||
# Bootstrapping Baïkal
|
||||
\Baikal\Framework::bootstrap();
|
||||
|
||||
if(!defined("BAIKAL_CAL_ENABLED") || BAIKAL_CAL_ENABLED !== TRUE) {
|
||||
throw new ErrorException("Baikal CalDAV is disabled.", 0, 255, __FILE__, __LINE__);
|
||||
}
|
||||
|
||||
$server = new \Baikal\Core\Server(
|
||||
BAIKAL_CAL_ENABLED,
|
||||
BAIKAL_CARD_ENABLED,
|
||||
BAIKAL_DAV_AUTH_TYPE,
|
||||
BAIKAL_AUTH_REALM,
|
||||
$GLOBALS['DB']->getPDO(),
|
||||
BAIKAL_CAL_BASEURI
|
||||
);
|
||||
$server->start();
|
|
@ -1 +0,0 @@
|
|||
../Core/Frameworks/Baikal/WWWRoot/card.php
|
62
html/card.php
Normal file
62
html/card.php
Normal file
|
@ -0,0 +1,62 @@
|
|||
<?php
|
||||
/***************************************************************
|
||||
* Copyright notice
|
||||
*
|
||||
* (c) 2013 Jérôme Schneider <mail@jeromeschneider.fr>
|
||||
* All rights reserved
|
||||
*
|
||||
* http://baikal-server.com
|
||||
*
|
||||
* This script is part of the Baïkal Server project. The Baïkal
|
||||
* Server project is free software; you can redistribute it
|
||||
* and/or modify it under the terms of the GNU General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2 of the License, or (at your option) any later version.
|
||||
*
|
||||
* The GNU General Public License can be found at
|
||||
* http://www.gnu.org/copyleft/gpl.html.
|
||||
*
|
||||
* This script is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* This copyright notice MUST APPEAR in all copies of the script!
|
||||
***************************************************************/
|
||||
|
||||
define("BAIKAL_CONTEXT", TRUE);
|
||||
define("PROJECT_CONTEXT_BASEURI", "/");
|
||||
|
||||
if(file_exists(getcwd() . "/Core")) {
|
||||
# Flat FTP mode
|
||||
define("PROJECT_PATH_ROOT", getcwd() . "/"); #./
|
||||
} else {
|
||||
# Dedicated server mode
|
||||
define("PROJECT_PATH_ROOT", dirname(getcwd()) . "/"); #../
|
||||
}
|
||||
|
||||
if(!file_exists(PROJECT_PATH_ROOT . 'vendor/')) {
|
||||
die('<h1>Incomplete installation</h1><p>Baïkal dependencies have not been installed. Please, execute "<strong>composer install</strong>" in the folder where you installed Baïkal.');
|
||||
}
|
||||
|
||||
require PROJECT_PATH_ROOT . 'vendor/autoload.php';
|
||||
|
||||
# Bootstraping Flake
|
||||
\Flake\Framework::bootstrap();
|
||||
|
||||
# Bootstrapping Baïkal
|
||||
\Baikal\Framework::bootstrap();
|
||||
|
||||
if(!defined("BAIKAL_CARD_ENABLED") || BAIKAL_CARD_ENABLED !== TRUE) {
|
||||
throw new ErrorException("Baikal CardDAV is disabled.", 0, 255, __FILE__, __LINE__);
|
||||
}
|
||||
|
||||
$server = new \Baikal\Core\Server(
|
||||
BAIKAL_CAL_ENABLED,
|
||||
BAIKAL_CARD_ENABLED,
|
||||
BAIKAL_DAV_AUTH_TYPE,
|
||||
BAIKAL_AUTH_REALM,
|
||||
$GLOBALS['DB']->getPDO(),
|
||||
BAIKAL_CARD_BASEURI
|
||||
);
|
||||
$server->start();
|
|
@ -25,10 +25,10 @@
|
|||
***************************************************************/
|
||||
|
||||
ini_set("session.cookie_httponly", 1);
|
||||
ini_set("display_errors", 0);
|
||||
ini_set("display_errors", 1);
|
||||
ini_set("log_errors", 1);
|
||||
|
||||
define("BAIKAL_CONTEXT", TRUE);
|
||||
define("BAIKAL_CONTEXT", true);
|
||||
define("PROJECT_CONTEXT_BASEURI", "/");
|
||||
|
||||
if(file_exists(getcwd() . "/Core")) {
|
||||
|
@ -42,49 +42,20 @@ if(file_exists(getcwd() . "/Core")) {
|
|||
if(!file_exists(PROJECT_PATH_ROOT . 'vendor/')) {
|
||||
die('<h1>Incomplete installation</h1><p>Baïkal dependencies have not been installed. Please, execute "<strong>composer install</strong>" in the folder where you installed Baïkal.');
|
||||
}
|
||||
|
||||
require PROJECT_PATH_ROOT . 'vendor/autoload.php';
|
||||
|
||||
# Bootstraping Flake
|
||||
\Flake\Framework::bootstrap();
|
||||
|
||||
# Bootstrapping Baïkal
|
||||
\Baikal\Framework::bootstrap();
|
||||
|
||||
if(!defined("BAIKAL_CAL_ENABLED") || BAIKAL_CAL_ENABLED !== TRUE) {
|
||||
throw new ErrorException("Baikal CalDAV is disabled.", 0, 255, __FILE__, __LINE__);
|
||||
}
|
||||
|
||||
# Backends
|
||||
if(
|
||||
BAIKAL_DAV_AUTH_TYPE == "Basic" || (
|
||||
array_key_exists('HTTP_USER_AGENT', $_SERVER) && (
|
||||
preg_match('/Windows-Phone-WebDAV-Client/i', $_SERVER['HTTP_USER_AGENT']) ||
|
||||
preg_match('/MSFT-WP\/8.10.*/i', $_SERVER['HTTP_USER_AGENT'])
|
||||
)
|
||||
)
|
||||
) {
|
||||
$authBackend = new \Baikal\Core\PDOBasicAuth($GLOBALS["DB"]->getPDO(), BAIKAL_AUTH_REALM);
|
||||
} else {
|
||||
$authBackend = new \Sabre\DAV\Auth\Backend\PDO($GLOBALS["DB"]->getPDO());
|
||||
}
|
||||
|
||||
$principalBackend = new \Sabre\DAVACL\PrincipalBackend\PDO($GLOBALS["DB"]->getPDO());
|
||||
$calendarBackend = new \Sabre\CalDAV\Backend\PDO($GLOBALS["DB"]->getPDO());
|
||||
|
||||
# Directory structure
|
||||
$nodes = array(
|
||||
new \Sabre\CalDAV\Principal\Collection($principalBackend),
|
||||
new \Sabre\CalDAV\CalendarRoot($principalBackend, $calendarBackend),
|
||||
$server = new \Baikal\Core\Server(
|
||||
BAIKAL_CAL_ENABLED,
|
||||
BAIKAL_CARD_ENABLED,
|
||||
BAIKAL_DAV_AUTH_TYPE,
|
||||
BAIKAL_AUTH_REALM,
|
||||
$GLOBALS['DB']->getPDO(),
|
||||
BAIKAL_DAV_BASEURI
|
||||
);
|
||||
|
||||
# Initializing server
|
||||
$server = new \Sabre\DAV\Server($nodes);
|
||||
$server->setBaseUri(BAIKAL_CAL_BASEURI);
|
||||
|
||||
# Server Plugins
|
||||
$server->addPlugin(new \Sabre\DAV\Auth\Plugin($authBackend, BAIKAL_AUTH_REALM));
|
||||
$server->addPlugin(new \Sabre\DAVACL\Plugin());
|
||||
$server->addPlugin(new \Sabre\CalDAV\Plugin());
|
||||
|
||||
# And off we go!
|
||||
$server->exec();
|
||||
$server->start();
|
Loading…
Reference in a new issue