mirror of
https://github.com/mclueppers/xo-server.git
synced 2025-07-29 17:04:44 +02:00
Initial commit.
This commit is contained in:
commit
abb2f50e10
4
.gitignore
vendored
Normal file
4
.gitignore
vendored
Normal file
@ -0,0 +1,4 @@
|
|||||||
|
/src/config/local.php
|
||||||
|
/src/database.json
|
||||||
|
/src/log
|
||||||
|
/vendor/
|
20
composer.json
Normal file
20
composer.json
Normal file
@ -0,0 +1,20 @@
|
|||||||
|
{
|
||||||
|
"name": "vates/xo-server",
|
||||||
|
"description": "Xen Orchestra server.",
|
||||||
|
"license": "GPL-3.0",
|
||||||
|
"authors": [
|
||||||
|
{
|
||||||
|
"name": "Julien Fontanet",
|
||||||
|
"email": "julien.fontanet@vates.fr"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"require": {
|
||||||
|
"php": ">=5.3.0",
|
||||||
|
"ext-xmlrpc": "*",
|
||||||
|
"monolog/monolog": "~1.2",
|
||||||
|
"ircmaxell/password-compat": "dev-master"
|
||||||
|
},
|
||||||
|
"autoload": {
|
||||||
|
"psr-0": { "": "src/lib" }
|
||||||
|
}
|
||||||
|
}
|
152
composer.lock
generated
Normal file
152
composer.lock
generated
Normal file
@ -0,0 +1,152 @@
|
|||||||
|
{
|
||||||
|
"hash": "d65aa7e3aad71264202dfcffcccb3cc1",
|
||||||
|
"packages": [
|
||||||
|
{
|
||||||
|
"name": "ircmaxell/password-compat",
|
||||||
|
"version": "dev-master",
|
||||||
|
"source": {
|
||||||
|
"type": "git",
|
||||||
|
"url": "https://github.com/ircmaxell/password_compat.git",
|
||||||
|
"reference": "fc4ad2d46794ace07cbf04fe654a8bf546fc6764"
|
||||||
|
},
|
||||||
|
"dist": {
|
||||||
|
"type": "zip",
|
||||||
|
"url": "https://api.github.com/repos/ircmaxell/password_compat/zipball/fc4ad2d46794ace07cbf04fe654a8bf546fc6764",
|
||||||
|
"reference": "fc4ad2d46794ace07cbf04fe654a8bf546fc6764",
|
||||||
|
"shasum": ""
|
||||||
|
},
|
||||||
|
"time": "2013-02-04 16:45:02",
|
||||||
|
"type": "library",
|
||||||
|
"autoload": {
|
||||||
|
"files": [
|
||||||
|
"lib/password.php"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"notification-url": "https://packagist.org/downloads/",
|
||||||
|
"license": [
|
||||||
|
"MIT"
|
||||||
|
],
|
||||||
|
"authors": [
|
||||||
|
{
|
||||||
|
"name": "Anthony Ferrara",
|
||||||
|
"email": "ircmaxell@php.net",
|
||||||
|
"homepage": "http://blog.ircmaxell.com"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"description": "A compatibility library for the proposed simplified password hashing algorithm: https://wiki.php.net/rfc/password_hash",
|
||||||
|
"homepage": "https://github.com/ircmaxell/password_compat",
|
||||||
|
"keywords": [
|
||||||
|
"hashing",
|
||||||
|
"password"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "monolog/monolog",
|
||||||
|
"version": "1.3.1",
|
||||||
|
"source": {
|
||||||
|
"type": "git",
|
||||||
|
"url": "https://github.com/Seldaek/monolog",
|
||||||
|
"reference": "1.3.1"
|
||||||
|
},
|
||||||
|
"dist": {
|
||||||
|
"type": "zip",
|
||||||
|
"url": "https://api.github.com/repos/Seldaek/monolog/zipball/1.3.1",
|
||||||
|
"reference": "1.3.1",
|
||||||
|
"shasum": ""
|
||||||
|
},
|
||||||
|
"require": {
|
||||||
|
"php": ">=5.3.0",
|
||||||
|
"psr/log": ">=1.0,<2.0"
|
||||||
|
},
|
||||||
|
"require-dev": {
|
||||||
|
"doctrine/couchdb": "dev-master",
|
||||||
|
"mlehner/gelf-php": "1.0.*",
|
||||||
|
"raven/raven": "0.3.*"
|
||||||
|
},
|
||||||
|
"suggest": {
|
||||||
|
"doctrine/couchdb": "Allow sending log messages to a CouchDB server",
|
||||||
|
"ext-amqp": "Allow sending log messages to an AMQP server (1.0+ required)",
|
||||||
|
"ext-mongo": "Allow sending log messages to a MongoDB server",
|
||||||
|
"mlehner/gelf-php": "Allow sending log messages to a GrayLog2 server",
|
||||||
|
"raven/raven": "Allow sending log messages to a Sentry server"
|
||||||
|
},
|
||||||
|
"time": "2013-01-11 10:23:20",
|
||||||
|
"type": "library",
|
||||||
|
"extra": {
|
||||||
|
"branch-alias": {
|
||||||
|
"dev-master": "1.3.x-dev"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"autoload": {
|
||||||
|
"psr-0": {
|
||||||
|
"Monolog": "src/"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"notification-url": "https://packagist.org/downloads/",
|
||||||
|
"license": [
|
||||||
|
"MIT"
|
||||||
|
],
|
||||||
|
"authors": [
|
||||||
|
{
|
||||||
|
"name": "Jordi Boggiano",
|
||||||
|
"email": "j.boggiano@seld.be",
|
||||||
|
"homepage": "http://seld.be",
|
||||||
|
"role": "Developer"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"description": "Sends your logs to files, sockets, inboxes, databases and various web services",
|
||||||
|
"homepage": "http://github.com/Seldaek/monolog",
|
||||||
|
"keywords": [
|
||||||
|
"log",
|
||||||
|
"logging",
|
||||||
|
"psr-3"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "psr/log",
|
||||||
|
"version": "1.0.0",
|
||||||
|
"source": {
|
||||||
|
"type": "git",
|
||||||
|
"url": "https://github.com/php-fig/log",
|
||||||
|
"reference": "1.0.0"
|
||||||
|
},
|
||||||
|
"dist": {
|
||||||
|
"type": "zip",
|
||||||
|
"url": "https://github.com/php-fig/log/archive/1.0.0.zip",
|
||||||
|
"reference": "1.0.0",
|
||||||
|
"shasum": ""
|
||||||
|
},
|
||||||
|
"time": "2012-12-21 11:40:51",
|
||||||
|
"type": "library",
|
||||||
|
"autoload": {
|
||||||
|
"psr-0": {
|
||||||
|
"Psr\\Log\\": ""
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"notification-url": "https://packagist.org/downloads/",
|
||||||
|
"license": [
|
||||||
|
"MIT"
|
||||||
|
],
|
||||||
|
"authors": [
|
||||||
|
{
|
||||||
|
"name": "PHP-FIG",
|
||||||
|
"homepage": "http://www.php-fig.org/"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"description": "Common interface for logging libraries",
|
||||||
|
"keywords": [
|
||||||
|
"log",
|
||||||
|
"psr",
|
||||||
|
"psr-3"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"packages-dev": null,
|
||||||
|
"aliases": [
|
||||||
|
|
||||||
|
],
|
||||||
|
"minimum-stability": "stable",
|
||||||
|
"stability-flags": {
|
||||||
|
"ircmaxell/password-compat": 20
|
||||||
|
}
|
||||||
|
}
|
39
src/config/global.php
Normal file
39
src/config/global.php
Normal file
@ -0,0 +1,39 @@
|
|||||||
|
<?php
|
||||||
|
/**
|
||||||
|
* This file is a part of Xen Orchestra Server.
|
||||||
|
*
|
||||||
|
* Xen Orchestra Server 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 3 of
|
||||||
|
* the License, or (at your option) any later version.
|
||||||
|
*
|
||||||
|
* Xen Orchestra Server 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.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with Xen Orchestra Server. If not, see
|
||||||
|
* <http://www.gnu.org/licenses/>.
|
||||||
|
*
|
||||||
|
* @author Julien Fontanet <julien.fontanet@vates.fr>
|
||||||
|
* @license http://www.gnu.org/licenses/gpl-3.0-standalone.html GPLv3
|
||||||
|
*
|
||||||
|
* @package Xen Orchestra Server
|
||||||
|
*/
|
||||||
|
|
||||||
|
return array(
|
||||||
|
|
||||||
|
'database' => array(
|
||||||
|
'json' => '#{root_dir}/database.json',
|
||||||
|
),
|
||||||
|
|
||||||
|
'listen' => array(
|
||||||
|
'tcp://0.0.0.0:1024',
|
||||||
|
),
|
||||||
|
|
||||||
|
'log' => array(
|
||||||
|
//'email' => 'your.email@example.net',
|
||||||
|
'file' => '#{root_dir}/log',
|
||||||
|
),
|
||||||
|
);
|
52
src/config/local.php.dist
Normal file
52
src/config/local.php.dist
Normal file
@ -0,0 +1,52 @@
|
|||||||
|
<?php
|
||||||
|
/**
|
||||||
|
* This file is a part of Xen Orchestra Server.
|
||||||
|
*
|
||||||
|
* Xen Orchestra Server 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 3 of
|
||||||
|
* the License, or (at your option) any later version.
|
||||||
|
*
|
||||||
|
* Xen Orchestra Server 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.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with Xen Orchestra Server. If not, see
|
||||||
|
* <http://www.gnu.org/licenses/>.
|
||||||
|
*
|
||||||
|
* @author Julien Fontanet <julien.fontanet@vates.fr>
|
||||||
|
* @license http://www.gnu.org/licenses/gpl-3.0-standalone.html GPLv3
|
||||||
|
*
|
||||||
|
* @package Xen Orchestra Server
|
||||||
|
*
|
||||||
|
* Local Variables:
|
||||||
|
* mode: php
|
||||||
|
* End:
|
||||||
|
*/
|
||||||
|
|
||||||
|
/* Local settings
|
||||||
|
*
|
||||||
|
* This file contains all settings related to this current
|
||||||
|
* installation of Xen Orchestra Server.
|
||||||
|
*
|
||||||
|
* You MUST define the following settings for which no default
|
||||||
|
* values exists.
|
||||||
|
*
|
||||||
|
* But, you MAY override any settings which already exists in
|
||||||
|
* “global.php”.
|
||||||
|
*/
|
||||||
|
return array(
|
||||||
|
|
||||||
|
// For now, XCP servers/pools must be defined in this file.
|
||||||
|
'xcp' => array(
|
||||||
|
'pool 1' => array(
|
||||||
|
'url' => 'https://xcp1.example.net',
|
||||||
|
'username' => 'username',
|
||||||
|
'password' => 'password'
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
|
||||||
|
|
863
src/lib/Application.php
Normal file
863
src/lib/Application.php
Normal file
@ -0,0 +1,863 @@
|
|||||||
|
<?php
|
||||||
|
/**
|
||||||
|
* This file is a part of Xen Orchestra Server.
|
||||||
|
*
|
||||||
|
* Xen Orchestra Server 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 3 of
|
||||||
|
* the License, or (at your option) any later version.
|
||||||
|
*
|
||||||
|
* Xen Orchestra Server 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.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with Xen Orchestra Server. If not, see
|
||||||
|
* <http://www.gnu.org/licenses/>.
|
||||||
|
*
|
||||||
|
* @author Julien Fontanet <julien.fontanet@vates.fr>
|
||||||
|
* @license http://www.gnu.org/licenses/gpl-3.0-standalone.html GPLv3
|
||||||
|
*
|
||||||
|
* @package Xen Orchestra Server
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
final class Application extends Base
|
||||||
|
{
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
const NONE = 0;
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
const READ = 1;
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
const WRITE = 2;
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
const ADMIN = 3;
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
function __construct(DI $di)
|
||||||
|
{
|
||||||
|
parent::__construct();
|
||||||
|
|
||||||
|
$this->_di = $di;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
function api_session_signInWithPassword($id, array $params, Client $c)
|
||||||
|
{
|
||||||
|
// Checks parameters.
|
||||||
|
if (!isset($params[0], $params[1]))
|
||||||
|
{
|
||||||
|
return -32602; // Invalid params.
|
||||||
|
}
|
||||||
|
list($name, $password) = $params;
|
||||||
|
|
||||||
|
// Checks the client is not already authenticated.
|
||||||
|
if ($c->isAuthenticated())
|
||||||
|
{
|
||||||
|
return array(0, 'already authenticated');
|
||||||
|
}
|
||||||
|
|
||||||
|
// Checks the user exists.
|
||||||
|
if (!isset($this->_usersByName[$name]))
|
||||||
|
{
|
||||||
|
return array(1, 'invalid credential');
|
||||||
|
}
|
||||||
|
|
||||||
|
$uid = $this->_usersByName[$name];
|
||||||
|
$hash = &$this->_users[$uid]['password'];
|
||||||
|
|
||||||
|
// Checks the password matches.
|
||||||
|
if (!password_verify($password, $hash))
|
||||||
|
{
|
||||||
|
return array(1, 'invalid credential');
|
||||||
|
}
|
||||||
|
|
||||||
|
// Checks whether the hash needs to be updated.
|
||||||
|
if (password_needs_rehash($hash, PASSWORD_DEFAULT))
|
||||||
|
{
|
||||||
|
$hash = password_hash($password, PASSWORD_DEFAULT);
|
||||||
|
$this->_saveDatabase();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Marks the client as authenticated.
|
||||||
|
$c->uid = $uid;
|
||||||
|
|
||||||
|
// Returns success.
|
||||||
|
$c->respond($id, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
function api_session_signInWithToken($id, array $params, Client $c)
|
||||||
|
{
|
||||||
|
// Checks parameters.
|
||||||
|
if (!isset($params[0]))
|
||||||
|
{
|
||||||
|
return -32602; // Invalid params.
|
||||||
|
}
|
||||||
|
$token = $params[0];
|
||||||
|
|
||||||
|
// Checks the client is not already authenticated.
|
||||||
|
if ($c->isAuthenticated())
|
||||||
|
{
|
||||||
|
return array(0, 'already authenticated');
|
||||||
|
}
|
||||||
|
|
||||||
|
// Checks the token exists.
|
||||||
|
if (!isset($this->_tokens[$token]))
|
||||||
|
{
|
||||||
|
return array(1, 'invalid token');
|
||||||
|
}
|
||||||
|
|
||||||
|
$record = $this->_tokens[$token];
|
||||||
|
|
||||||
|
// Checks the token is valid.
|
||||||
|
if ($record['expiration'] < time())
|
||||||
|
{
|
||||||
|
unset($this->_tokens[$token]);
|
||||||
|
return array(1, 'invalid token');
|
||||||
|
}
|
||||||
|
|
||||||
|
// Marks the client as authenticated.
|
||||||
|
$c->uid = $record['uid'];
|
||||||
|
|
||||||
|
// Returns success.
|
||||||
|
$c->respond($id, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
function api_session_getUser($id, array $params, Client $c)
|
||||||
|
{
|
||||||
|
if (!$c->isAuthenticated())
|
||||||
|
{
|
||||||
|
return array(0, 'not authenticated');
|
||||||
|
}
|
||||||
|
|
||||||
|
$c->respond($id, array(
|
||||||
|
'id' => (string) $c->uid,
|
||||||
|
'name' => $this->_users[$c->uid]['name'],
|
||||||
|
'permission' => $this->_users[$c->uid]['permission'],
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
function api_session_createToken($id, array $params, Client $c)
|
||||||
|
{
|
||||||
|
// Checks the client is authenticated.
|
||||||
|
if (!$c->isAuthenticated())
|
||||||
|
{
|
||||||
|
return array(0, 'not authenticated');
|
||||||
|
}
|
||||||
|
|
||||||
|
// Generates the token and makes sure it is unique.
|
||||||
|
do
|
||||||
|
{
|
||||||
|
/* If available, we use OpenSSL to create more secure tokens.
|
||||||
|
*
|
||||||
|
* @todo Maybe we should also use a time-resistant token comparison
|
||||||
|
* algorithm in authentication.
|
||||||
|
*
|
||||||
|
* @todo Move the if outside of this function and furthermore of
|
||||||
|
* this loop for performance concerns.
|
||||||
|
*/
|
||||||
|
if (function_exists('openssl_random_pseudo_bytes'))
|
||||||
|
{
|
||||||
|
$token = bin2hex(openssl_random_pseudo_bytes(32));
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
$token = uniqid('', true);
|
||||||
|
}
|
||||||
|
} while (isset($this->_tokens[$token]));
|
||||||
|
|
||||||
|
// Registers it.
|
||||||
|
$this->_tokens[$token] = array(
|
||||||
|
'expiration' => time() + 604800, // One week
|
||||||
|
'uid' => $c->uid,
|
||||||
|
);
|
||||||
|
$this->_saveDatabase();
|
||||||
|
|
||||||
|
// Returns it.
|
||||||
|
$c->respond($id, $token);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
function api_session_destroyToken($id, array $params, Client $c)
|
||||||
|
{
|
||||||
|
// Checks the token exists.
|
||||||
|
if (!isset($this->_tokens[$token]))
|
||||||
|
{
|
||||||
|
return array(0, 'invalid token');
|
||||||
|
}
|
||||||
|
|
||||||
|
// Deletes it.
|
||||||
|
unset($this->_tokens[$token]);
|
||||||
|
$this->_saveDatabase();
|
||||||
|
|
||||||
|
// Returns success.
|
||||||
|
$c->respond($id, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
function api_user_create($id, array $params, Client $c)
|
||||||
|
{
|
||||||
|
// Checks parameters.
|
||||||
|
if (!isset($params[0], $params[1]))
|
||||||
|
{
|
||||||
|
return -32602; // Invalid params.
|
||||||
|
}
|
||||||
|
list($name, $password) = $params;
|
||||||
|
|
||||||
|
// Checks credentials.
|
||||||
|
if (!$c->isAuthenticated()
|
||||||
|
|| !$this->_checkPermission($c->uid, self::ADMIN))
|
||||||
|
{
|
||||||
|
return array(0, 'not authorized');
|
||||||
|
}
|
||||||
|
|
||||||
|
// Checks the provided user name.
|
||||||
|
if (!is_string($name)
|
||||||
|
|| !preg_match('/^[a-z0-9]+(?:[-_.][a-z0-9]+)*$/', $name))
|
||||||
|
{
|
||||||
|
return array(1, 'invalid user name');
|
||||||
|
}
|
||||||
|
|
||||||
|
// Checks the provided password.
|
||||||
|
if (!is_string($password)
|
||||||
|
|| !preg_match('/^.{8,}$/', $password))
|
||||||
|
{
|
||||||
|
return array(2, 'invalid password');
|
||||||
|
}
|
||||||
|
|
||||||
|
// Checks provided permission.
|
||||||
|
if (isset($params[2]))
|
||||||
|
{
|
||||||
|
$permission = self::_permissionFromString($params[2]);
|
||||||
|
if ($permission === false)
|
||||||
|
{
|
||||||
|
return array(3, 'invalid permission');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
$permission = self::NONE;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Checks if the user name is already used.
|
||||||
|
if (isset($this->_usersByName[$name]))
|
||||||
|
{
|
||||||
|
return array(4, 'user name already taken');
|
||||||
|
}
|
||||||
|
|
||||||
|
// Creates the user.
|
||||||
|
$this->_users[] = array(
|
||||||
|
'name' => $name,
|
||||||
|
'password' => password_hash($password, PASSWORD_DEFAULT),
|
||||||
|
'permission' => $permission,
|
||||||
|
);
|
||||||
|
end($this->_users);
|
||||||
|
$uid = (string) key($this->_users);
|
||||||
|
$this->_usersByName[$name] = $uid;
|
||||||
|
$this->_saveDatabase();
|
||||||
|
|
||||||
|
// Returns the identifier.
|
||||||
|
$c->respond($id, $uid);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
function api_user_delete($id, array $params, Client $c)
|
||||||
|
{
|
||||||
|
// Checks parameter.
|
||||||
|
if (!isset($params[0]))
|
||||||
|
{
|
||||||
|
return -32602; // Invalid params.
|
||||||
|
}
|
||||||
|
$uid = $params[0];
|
||||||
|
|
||||||
|
// Checks credentials.
|
||||||
|
if (!$c->isAuthenticated()
|
||||||
|
|| !$this->_checkPermission($c->uid, self::ADMIN))
|
||||||
|
{
|
||||||
|
return array(0, 'not authorized');
|
||||||
|
}
|
||||||
|
|
||||||
|
// Checks user exists and is not the current user.
|
||||||
|
if (!isset($this->_users[$uid])
|
||||||
|
|| ($uid === $c->uid))
|
||||||
|
{
|
||||||
|
return array(1, 'invalid user');
|
||||||
|
}
|
||||||
|
|
||||||
|
// Deletes the user.
|
||||||
|
$name = $this->_users[$uid]['name'];
|
||||||
|
unset($this->_users[$uid], $this->_usersByName[$name]);
|
||||||
|
$this->_saveDatabase();
|
||||||
|
|
||||||
|
// Returns success.
|
||||||
|
$c->respond($id, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
function api_user_changePassword($id, array $params, Client $c)
|
||||||
|
{
|
||||||
|
// Checks parameters.
|
||||||
|
if (!isset($params[0], $params[1]))
|
||||||
|
{
|
||||||
|
return -32602; // Invalid params.
|
||||||
|
}
|
||||||
|
list($old, $new) = $params;
|
||||||
|
|
||||||
|
// Checks the client is authenticated.
|
||||||
|
if (!$c->isAuthenticated())
|
||||||
|
{
|
||||||
|
return array(0, 'not authenticated');
|
||||||
|
}
|
||||||
|
|
||||||
|
$hash = &$this->_users[$c->uid]['password'];
|
||||||
|
|
||||||
|
// Checks the old password matches.
|
||||||
|
if (!password_verify($old, $hash))
|
||||||
|
{
|
||||||
|
return array(1, 'invalid credential');
|
||||||
|
}
|
||||||
|
|
||||||
|
// Checks the new password is valid.
|
||||||
|
if (!is_string($new)
|
||||||
|
|| !preg_match('/^.{8,}$/', $new))
|
||||||
|
{
|
||||||
|
return array(2, 'invalid password');
|
||||||
|
}
|
||||||
|
|
||||||
|
$hash = password_hash($new, PASSWORD_DEFAULT);
|
||||||
|
$this->_saveDatabase();
|
||||||
|
|
||||||
|
// Returns success.
|
||||||
|
$c->respond($id, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
function api_user_getAll($id, array $params, Client $c)
|
||||||
|
{
|
||||||
|
// Checks credentials.
|
||||||
|
if (!$c->isAuthenticated()
|
||||||
|
|| !$this->_checkPermission($c->uid, self::ADMIN))
|
||||||
|
{
|
||||||
|
return array(0, 'not authorized');
|
||||||
|
}
|
||||||
|
|
||||||
|
$users = array();
|
||||||
|
foreach ($this->_users as $uid => $user)
|
||||||
|
{
|
||||||
|
$users[] = array(
|
||||||
|
'id' => $uid,
|
||||||
|
'name' => $user['name'],
|
||||||
|
'permission' => self::_permissionToString($user['permission']),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
$c->respond($id, $users);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
function api_vm_getAll($id, array $params, Client $c)
|
||||||
|
{
|
||||||
|
// @todo Handles parameter.
|
||||||
|
|
||||||
|
$c->respond($id, $this->_xenVms);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
function handleServer($handle, $data)
|
||||||
|
{
|
||||||
|
if (feof($handle))
|
||||||
|
{
|
||||||
|
// Stops listening to this socket.
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
$handle = @stream_socket_accept($handle, 10);
|
||||||
|
if (!$handle)
|
||||||
|
{
|
||||||
|
trigger_error(
|
||||||
|
'error while handling an incoming connection',
|
||||||
|
E_USER_ERROR
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Here we build a map for all available methods.
|
||||||
|
*
|
||||||
|
* This technic provides fast case sensitive matching (compare to
|
||||||
|
* “is_callable()”).
|
||||||
|
*/
|
||||||
|
static $methods;
|
||||||
|
if ($methods === null)
|
||||||
|
{
|
||||||
|
$methods = array();
|
||||||
|
foreach (get_class_methods($this) as $method)
|
||||||
|
{
|
||||||
|
if (!substr_compare($method, 'api_', 0, 4))
|
||||||
|
{
|
||||||
|
$_ = strtr(substr($method, 4), '_', '.');
|
||||||
|
$methods[$_] = array($this, $method);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
new Client(
|
||||||
|
$data['loop'],
|
||||||
|
$handle,
|
||||||
|
$methods
|
||||||
|
);
|
||||||
|
|
||||||
|
echo "new client connected\n";
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
function handleXenEvents(array $events)
|
||||||
|
{
|
||||||
|
static $keys;
|
||||||
|
|
||||||
|
$objects = array();
|
||||||
|
|
||||||
|
foreach ($events as $event)
|
||||||
|
{
|
||||||
|
$_ = array_keys($event);
|
||||||
|
if (!$keys)
|
||||||
|
{
|
||||||
|
$keys = $_;
|
||||||
|
var_export($keys); echo PHP_EOL;
|
||||||
|
}
|
||||||
|
elseif ($_ !== $keys)
|
||||||
|
{
|
||||||
|
$keys = array_intersect($keys, $_);
|
||||||
|
var_export($keys); echo PHP_EOL;
|
||||||
|
}
|
||||||
|
|
||||||
|
$class = $event['class'];
|
||||||
|
$ref = $event['ref'];
|
||||||
|
$snapshot = $event['snapshot']; // Not present in the documentation.
|
||||||
|
|
||||||
|
echo "$class - $ref\n";
|
||||||
|
|
||||||
|
$objects[$class][$ref] = $snapshot;
|
||||||
|
}
|
||||||
|
|
||||||
|
isset($objects['pool'])
|
||||||
|
and $this->updateXenPools($objects['pool']);
|
||||||
|
isset($objects['host'])
|
||||||
|
and $this->updateXenHosts($objects['host']);
|
||||||
|
isset($objects['vm'])
|
||||||
|
and $this->updateXenVms($objects['vm']);
|
||||||
|
|
||||||
|
// Requeue this request.
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
function updateXenPools(array $pools)
|
||||||
|
{
|
||||||
|
foreach ($pools as $ref => $pool)
|
||||||
|
{
|
||||||
|
$this->_update($this->_xenPools[$ref], $pool);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
function updateXenHosts(array $hosts)
|
||||||
|
{
|
||||||
|
foreach ($hosts as $ref => $host)
|
||||||
|
{
|
||||||
|
$this->_update($this->_xenHosts[$ref], $host);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
function updateXenVms(array $vms)
|
||||||
|
{
|
||||||
|
foreach ($vms as $ref => $vm)
|
||||||
|
{
|
||||||
|
if ($vm['is_a_template'])
|
||||||
|
{
|
||||||
|
$_ = 'template';
|
||||||
|
}
|
||||||
|
elseif ($vm['is_a_snapshot'])
|
||||||
|
{
|
||||||
|
$_ = 'snapshot';
|
||||||
|
}
|
||||||
|
elseif ($vm['is_control_domain'])
|
||||||
|
{
|
||||||
|
$_ = 'control_domain';
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
$_ = 'normal';
|
||||||
|
}
|
||||||
|
|
||||||
|
$this->_update(
|
||||||
|
$this->_xenVms[$_][$ref],
|
||||||
|
$vm
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
function run()
|
||||||
|
{
|
||||||
|
$this->_loadDatabase();
|
||||||
|
|
||||||
|
//--------------------------------------
|
||||||
|
|
||||||
|
$config = $this->_di->get('config');
|
||||||
|
$loop = $this->_di->get('loop');
|
||||||
|
|
||||||
|
//--------------------------------------
|
||||||
|
|
||||||
|
// Creates master sockets.
|
||||||
|
foreach ($config->get('listen') as $uri)
|
||||||
|
{
|
||||||
|
$handle = self::_createServer($uri);
|
||||||
|
$loop->addRead($handle, array($this, 'handleServer'));
|
||||||
|
}
|
||||||
|
|
||||||
|
//--------------------------------------
|
||||||
|
|
||||||
|
foreach ($config->get('xcp') as $_)
|
||||||
|
{
|
||||||
|
$xcp = new XCP($loop, $_['url'], $_['username'], $_['password']);
|
||||||
|
$xcp->queue(
|
||||||
|
'VM.get_all_records',
|
||||||
|
null,
|
||||||
|
array($this, 'updateXenVms')
|
||||||
|
);
|
||||||
|
$xcp->queue(
|
||||||
|
'event.register',
|
||||||
|
array(array('host', 'pool', 'vm'))
|
||||||
|
);
|
||||||
|
$xcp->queue(
|
||||||
|
'event.next',
|
||||||
|
null,
|
||||||
|
array($this, 'handleXenEvents')
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
//--------------------------------------
|
||||||
|
|
||||||
|
$loop->run(array(
|
||||||
|
'loop' => $loop,
|
||||||
|
'server' => $this
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
private static function _createServer($uri)
|
||||||
|
{
|
||||||
|
list($transport, $target) = explode('://', $uri, 2);
|
||||||
|
|
||||||
|
if (($transport === 'unix')
|
||||||
|
|| ($transport === 'udg'))
|
||||||
|
{
|
||||||
|
@unlink($target);
|
||||||
|
}
|
||||||
|
|
||||||
|
$handle = @stream_socket_server(
|
||||||
|
$uri,
|
||||||
|
/* out */ $errno,
|
||||||
|
/* out */ $errstr
|
||||||
|
);
|
||||||
|
|
||||||
|
if (!$handle)
|
||||||
|
{
|
||||||
|
trigger_error(
|
||||||
|
"could not create the server socket $uri: $errno - $errstr",
|
||||||
|
E_USER_ERROR
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
return $handle;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Dependency injector.
|
||||||
|
*
|
||||||
|
* @var DI
|
||||||
|
*/
|
||||||
|
private $_di;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Mapping from user identifier to record.
|
||||||
|
*
|
||||||
|
* Each record contains:
|
||||||
|
* - “name” (string): the user name used for sign in;
|
||||||
|
* - “password” (string): the user password hashed for sign in.
|
||||||
|
*
|
||||||
|
* @var array
|
||||||
|
*/
|
||||||
|
private $_users = array();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Mapping from user name to identifier.
|
||||||
|
*
|
||||||
|
* @var array
|
||||||
|
*/
|
||||||
|
private $_usersByName = array();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Tokens that may be used to authenticate clients.
|
||||||
|
*
|
||||||
|
* Each token record is an array containing:
|
||||||
|
* - “expiration” (integer): timestamp of when this token will be
|
||||||
|
* considered invalid;
|
||||||
|
* - “uid” (string): the identifier of the user authenticated with
|
||||||
|
* this token.
|
||||||
|
*
|
||||||
|
* @var array
|
||||||
|
*/
|
||||||
|
private $_tokens = array();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var array
|
||||||
|
*/
|
||||||
|
private $_xenPools = array();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var array
|
||||||
|
*/
|
||||||
|
private $_xenHosts = array();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var array
|
||||||
|
*/
|
||||||
|
private $_xenVms = array();
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
private static function _tS($val)
|
||||||
|
{
|
||||||
|
if (is_scalar($val))
|
||||||
|
{
|
||||||
|
return (string) $val;
|
||||||
|
}
|
||||||
|
return gettype($val);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
private static function _permissionFromString($string)
|
||||||
|
{
|
||||||
|
$permissions = array(
|
||||||
|
'none' => self::NONE,
|
||||||
|
'read' => self::READ,
|
||||||
|
'write' => self::WRITE,
|
||||||
|
'admin' => self::ADMIN
|
||||||
|
);
|
||||||
|
|
||||||
|
return isset($permissions[$string])
|
||||||
|
? $permissions[$string]
|
||||||
|
: false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
private static function _permissionToString($permission)
|
||||||
|
{
|
||||||
|
$permissions = array(
|
||||||
|
self::NONE => 'none',
|
||||||
|
self::READ => 'read',
|
||||||
|
self::WRITE => 'write',
|
||||||
|
self::ADMIN => 'admin',
|
||||||
|
);
|
||||||
|
|
||||||
|
return isset($permissions[$permission])
|
||||||
|
? $permissions[$permission]
|
||||||
|
: false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
private function _update(&$old, $new)
|
||||||
|
{
|
||||||
|
// There was no previous record.
|
||||||
|
if ($old === null)
|
||||||
|
{
|
||||||
|
echo "new record\n";
|
||||||
|
$old = $new;
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// The record has been deleted.
|
||||||
|
if ($new === null)
|
||||||
|
{
|
||||||
|
echo "record deleted\n";
|
||||||
|
$old = null;
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
$old_keys = array_keys($old);
|
||||||
|
$new_keys = array_keys($new);
|
||||||
|
|
||||||
|
foreach (array_diff($old_keys, $new_keys) as $key)
|
||||||
|
{
|
||||||
|
$_ = self::_tS($old[$key]);
|
||||||
|
echo "field removed: $key => $_\n";
|
||||||
|
}
|
||||||
|
foreach (array_diff($new_keys, $old_keys) as $key)
|
||||||
|
{
|
||||||
|
$_ = self::_tS($new[$key]);
|
||||||
|
echo "field added: $key => $_\n";
|
||||||
|
}
|
||||||
|
foreach (array_intersect($new_keys, $old_keys) as $key)
|
||||||
|
{
|
||||||
|
if ($new[$key] === $old[$key])
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
$_1 = self::_tS($old[$key]);
|
||||||
|
$_2 = self::_tS($new[$key]);
|
||||||
|
echo "field changed: $key => $_1 → $_2\n";
|
||||||
|
}
|
||||||
|
$old = $new;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
private function _saveDatabase()
|
||||||
|
{
|
||||||
|
$data = json_encode(array(
|
||||||
|
'users' => $this->_users,
|
||||||
|
'usersByName' => $this->_usersByName,
|
||||||
|
'tokens' => $this->_tokens,
|
||||||
|
));
|
||||||
|
|
||||||
|
$bytes = @file_put_contents(
|
||||||
|
$this->_di->get('config')->get('database.json'),
|
||||||
|
$data
|
||||||
|
);
|
||||||
|
if ($bytes === false)
|
||||||
|
{
|
||||||
|
trigger_error(
|
||||||
|
'could not write the database',
|
||||||
|
E_USER_ERROR
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
private function _loadDatabase()
|
||||||
|
{
|
||||||
|
$file = $this->_di->get('config')->get('database.json');
|
||||||
|
if (!file_exists($file))
|
||||||
|
{
|
||||||
|
trigger_error(
|
||||||
|
'no such database, using default values (admin:admin)',
|
||||||
|
E_USER_WARNING
|
||||||
|
);
|
||||||
|
|
||||||
|
// @todo Factorizes this code with api_user_create().
|
||||||
|
|
||||||
|
$this->_users = array(
|
||||||
|
1 => array(
|
||||||
|
'name' => 'admin',
|
||||||
|
'password' => '$2y$10$VzBQqiwnhG5zc2.MQmmW4ORcPW6FE7SLhPr1VBV2ubn5zJoesnmli',
|
||||||
|
'permission' => self::ADMIN,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
$this->_usersByName = array(
|
||||||
|
'admin' => '1',
|
||||||
|
);
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
$data = @file_get_contents(
|
||||||
|
$this->_di->get('config')->get('database.json')
|
||||||
|
);
|
||||||
|
if (($data === false)
|
||||||
|
|| (($data = json_decode($data, true)) === null))
|
||||||
|
{
|
||||||
|
trigger_error(
|
||||||
|
'could not read the database',
|
||||||
|
E_USER_ERROR
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach (array('users', 'usersByName', 'tokens') as $entry)
|
||||||
|
{
|
||||||
|
if (!isset($data[$entry]))
|
||||||
|
{
|
||||||
|
trigger_error(
|
||||||
|
"missing entry from the database: $entry",
|
||||||
|
E_USER_ERROR
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
$this->{'_'.$entry} = $data[$entry];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
private function _checkPermission($uid, $permission, $object = null)
|
||||||
|
{
|
||||||
|
return ($this->_users[$uid]['permission'] >= $permission);
|
||||||
|
}
|
||||||
|
}
|
51
src/lib/Base.php
Normal file
51
src/lib/Base.php
Normal file
@ -0,0 +1,51 @@
|
|||||||
|
<?php
|
||||||
|
/**
|
||||||
|
* This file is a part of Xen Orchestra Server.
|
||||||
|
*
|
||||||
|
* Xen Orchestra Server 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 3 of
|
||||||
|
* the License, or (at your option) any later version.
|
||||||
|
*
|
||||||
|
* Xen Orchestra Server 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.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with Xen Orchestra Server. If not, see
|
||||||
|
* <http://www.gnu.org/licenses/>.
|
||||||
|
*
|
||||||
|
* @author Julien Fontanet <julien.fontanet@vates.fr>
|
||||||
|
* @license http://www.gnu.org/licenses/gpl-3.0-standalone.html GPLv3
|
||||||
|
*
|
||||||
|
* @package Xen Orchestra Server
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Ultimate base class.
|
||||||
|
*/
|
||||||
|
abstract class Base
|
||||||
|
{
|
||||||
|
function __destruct()
|
||||||
|
{}
|
||||||
|
|
||||||
|
function __get($name)
|
||||||
|
{
|
||||||
|
trigger_error(
|
||||||
|
'no such readable property '.get_class($this).'->'.$name,
|
||||||
|
E_USER_ERROR
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
function __set($name, $value)
|
||||||
|
{
|
||||||
|
trigger_error(
|
||||||
|
'no such writable property '.get_class($this).'->'.$name,
|
||||||
|
E_USER_ERROR
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function __construct()
|
||||||
|
{}
|
||||||
|
}
|
65
src/lib/BufferedWriter.php
Normal file
65
src/lib/BufferedWriter.php
Normal file
@ -0,0 +1,65 @@
|
|||||||
|
<?php
|
||||||
|
/**
|
||||||
|
* This file is a part of Xen Orchestra Server.
|
||||||
|
*
|
||||||
|
* Xen Orchestra Server 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 3 of
|
||||||
|
* the License, or (at your option) any later version.
|
||||||
|
*
|
||||||
|
* Xen Orchestra Server 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.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with Xen Orchestra Server. If not, see
|
||||||
|
* <http://www.gnu.org/licenses/>.
|
||||||
|
*
|
||||||
|
* @author Julien Fontanet <julien.fontanet@vates.fr>
|
||||||
|
* @license http://www.gnu.org/licenses/gpl-3.0-standalone.html GPLv3
|
||||||
|
*
|
||||||
|
* @package Xen Orchestra Server
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
final class BufferedWriter
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* @param string $data
|
||||||
|
*/
|
||||||
|
function __construct($data, $len = null)
|
||||||
|
{
|
||||||
|
$this->_data = $data;
|
||||||
|
$this->_len = $len ?: strlen($data);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
function onWrite($handle)
|
||||||
|
{
|
||||||
|
$written = @fwrite(
|
||||||
|
$handle,
|
||||||
|
$this->_data,
|
||||||
|
$this->_len
|
||||||
|
);
|
||||||
|
|
||||||
|
if ($written === false)
|
||||||
|
{
|
||||||
|
// @todo Log error.
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
$this->_len -= $written;
|
||||||
|
if (!$this->_len)
|
||||||
|
{
|
||||||
|
// Write complete, stops watching this handle.
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
$this->_data = substr($this->_data, -$this->_len);
|
||||||
|
}
|
||||||
|
}
|
286
src/lib/Client.php
Normal file
286
src/lib/Client.php
Normal file
@ -0,0 +1,286 @@
|
|||||||
|
<?php
|
||||||
|
/**
|
||||||
|
* This file is a part of Xen Orchestra Server.
|
||||||
|
*
|
||||||
|
* Xen Orchestra Server 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 3 of
|
||||||
|
* the License, or (at your option) any later version.
|
||||||
|
*
|
||||||
|
* Xen Orchestra Server 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.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with Xen Orchestra Server. If not, see
|
||||||
|
* <http://www.gnu.org/licenses/>.
|
||||||
|
*
|
||||||
|
* @author Julien Fontanet <julien.fontanet@vates.fr>
|
||||||
|
* @license http://www.gnu.org/licenses/gpl-3.0-standalone.html GPLv3
|
||||||
|
*
|
||||||
|
* @package Xen Orchestra Server
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
final class Client extends Base
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
public $uid = 0;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param Loop $loop
|
||||||
|
* @param resource $handle
|
||||||
|
* @param callable[] $methods
|
||||||
|
*/
|
||||||
|
function __construct(Loop $loop, $handle, $methods)
|
||||||
|
{
|
||||||
|
parent::__construct();
|
||||||
|
|
||||||
|
$this->_loop = $loop;
|
||||||
|
$this->_handle = $handle;
|
||||||
|
$this->_methods = $methods;
|
||||||
|
|
||||||
|
stream_set_blocking($this->_handle, false);
|
||||||
|
$loop->addRead($this->_handle, array($this, '_onRead'));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
function isAuthenticated()
|
||||||
|
{
|
||||||
|
return ($this->uid !== 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
function notify($method, array $params)
|
||||||
|
{
|
||||||
|
$this->_send(array(
|
||||||
|
'method' => $method,
|
||||||
|
'params' => $params
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
function respond($id, $result)
|
||||||
|
{
|
||||||
|
if ($id === null)
|
||||||
|
{
|
||||||
|
trigger_error(
|
||||||
|
'notifications do not expect responses',
|
||||||
|
E_USER_ERROR
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
$this->_send(array(
|
||||||
|
'id' => $id,
|
||||||
|
'result' => $result
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
|
////////////////////////////////////////
|
||||||
|
//
|
||||||
|
////////////////////////////////////////
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This function is called when data become available on the
|
||||||
|
* connection.
|
||||||
|
*
|
||||||
|
* Its name starts with a “_” because it is only supposed to be
|
||||||
|
* used internally (it is not private because of PHP limits).
|
||||||
|
*/
|
||||||
|
function _onRead()
|
||||||
|
{
|
||||||
|
// Read the message length.
|
||||||
|
if (!$this->_len)
|
||||||
|
{
|
||||||
|
$len = stream_get_line($this->_handle, 1024, "\n");
|
||||||
|
if ($len === false)
|
||||||
|
{
|
||||||
|
if (feof($this->_handle))
|
||||||
|
{
|
||||||
|
echo "client disconnected\n";
|
||||||
|
|
||||||
|
// Closes this socket and stops listening to it.
|
||||||
|
stream_socket_shutdown($this->_handle, STREAM_SHUT_RDWR);
|
||||||
|
fclose($this->_handle);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
$this->_error('failed to read the request length');
|
||||||
|
}
|
||||||
|
if (!ctype_digit($len)
|
||||||
|
|| ($len <= 0))
|
||||||
|
{
|
||||||
|
$this->_error('invalid request length');
|
||||||
|
}
|
||||||
|
|
||||||
|
$this->_len = (int) $len;
|
||||||
|
}
|
||||||
|
|
||||||
|
$buf = @fread($this->_handle, $this->_len);
|
||||||
|
if ($buf === false)
|
||||||
|
{
|
||||||
|
$this->_error('failed to read the request');
|
||||||
|
}
|
||||||
|
$this->_buf .= $buf;
|
||||||
|
$this->_len -= strlen($buf);
|
||||||
|
|
||||||
|
if ($this->_len !== 0)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
$request = @json_decode($this->_buf, true);
|
||||||
|
$this->_buf = '';
|
||||||
|
|
||||||
|
if ($request === null)
|
||||||
|
{
|
||||||
|
$this->_warning(null, -32700, 'Parse error');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!isset($request['jsonrpc'], $request['method'])
|
||||||
|
|| ($request['jsonrpc'] !== '2.0'))
|
||||||
|
{
|
||||||
|
$this->_warning(null, -32600, 'Invalid request');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
$id = isset($request['id']) ? $request['id'] : null;
|
||||||
|
$method = $request['method'];
|
||||||
|
$params = isset($request['params']) ? $request['params'] : array();
|
||||||
|
|
||||||
|
if (isset($this->_methods[$method]))
|
||||||
|
{
|
||||||
|
$_ = $this->_methods[$method];
|
||||||
|
$error = $_($id, $params, $this);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
$error = array(-32601, 'Method not found', $method);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (($error === null)
|
||||||
|
|| ($id === null))
|
||||||
|
{
|
||||||
|
// No errors or this was a notification (no error handling).
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
$errors = array(
|
||||||
|
-32602 => array(
|
||||||
|
'Invalid params',
|
||||||
|
$params
|
||||||
|
),
|
||||||
|
);
|
||||||
|
if (is_numeric($error) && isset($errors[$error]))
|
||||||
|
{
|
||||||
|
$error = $errors[$error];
|
||||||
|
}
|
||||||
|
elseif (!is_array($error) || !isset($error[0], $error[1]))
|
||||||
|
{
|
||||||
|
trigger_error(
|
||||||
|
'invalid error',
|
||||||
|
E_USER_ERROR
|
||||||
|
);
|
||||||
|
}
|
||||||
|
$this->_warning(
|
||||||
|
$id,
|
||||||
|
$error[0],
|
||||||
|
$error[1],
|
||||||
|
isset($error[2]) ? $error[2] : null
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var Loop
|
||||||
|
*/
|
||||||
|
private $_loop;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var resource
|
||||||
|
*/
|
||||||
|
private $_handle;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var callable[]
|
||||||
|
*/
|
||||||
|
private $_methods;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var integer
|
||||||
|
*/
|
||||||
|
private $_len;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var string|null
|
||||||
|
*/
|
||||||
|
private $_buf;
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
private function _error($message)
|
||||||
|
{
|
||||||
|
throw new Exception($message);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param array $message
|
||||||
|
*/
|
||||||
|
private function _send($message)
|
||||||
|
{
|
||||||
|
$message['jsonrpc'] = '2.0';
|
||||||
|
$message = json_encode($message);
|
||||||
|
|
||||||
|
$data = strlen($message)."\n".$message;
|
||||||
|
$len = strlen($data);
|
||||||
|
|
||||||
|
$written = @fwrite($this->_handle, $data, $len);
|
||||||
|
if ($written === false)
|
||||||
|
{
|
||||||
|
$this->_error('failed to send the message');
|
||||||
|
}
|
||||||
|
|
||||||
|
$len -= $written;
|
||||||
|
if ($len)
|
||||||
|
{
|
||||||
|
echo "$len bytes to write, using a buffered writer\n";
|
||||||
|
$this->_loop->addWrite(
|
||||||
|
$this->_handle,
|
||||||
|
array(
|
||||||
|
new BufferedWriter(substr($data, -$len), $len),
|
||||||
|
'onWrite'
|
||||||
|
)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
private function _warning($id, $code, $message, $data = null)
|
||||||
|
{
|
||||||
|
$message = array(
|
||||||
|
'id' => $id,
|
||||||
|
'error' => array(
|
||||||
|
'code' => $code,
|
||||||
|
'message' => $message
|
||||||
|
)
|
||||||
|
);
|
||||||
|
($data !== null)
|
||||||
|
and $message['error']['data'] = $data;
|
||||||
|
|
||||||
|
$this->_send($message);
|
||||||
|
}
|
||||||
|
}
|
159
src/lib/Config.php
Normal file
159
src/lib/Config.php
Normal file
@ -0,0 +1,159 @@
|
|||||||
|
<?php
|
||||||
|
/**
|
||||||
|
* This file is a part of Xen Orchestra Server.
|
||||||
|
*
|
||||||
|
* Xen Orchestra Server 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 3 of
|
||||||
|
* the License, or (at your option) any later version.
|
||||||
|
*
|
||||||
|
* Xen Orchestra Server 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.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with Xen Orchestra Server. If not, see
|
||||||
|
* <http://www.gnu.org/licenses/>.
|
||||||
|
*
|
||||||
|
* @author Julien Fontanet <julien.fontanet@vates.fr>
|
||||||
|
* @license http://www.gnu.org/licenses/gpl-3.0-standalone.html GPLv3
|
||||||
|
*
|
||||||
|
* @package Xen Orchestra Server
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
final class Config extends Base
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
function __construct(array $entries = null)
|
||||||
|
{
|
||||||
|
parent::__construct();
|
||||||
|
|
||||||
|
$this->_entries = isset($entries) ? $entries : array();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns an entry.
|
||||||
|
*
|
||||||
|
* @param string $path
|
||||||
|
* @param mixed $default Optional.
|
||||||
|
*
|
||||||
|
* @return array
|
||||||
|
*
|
||||||
|
* @throws Exception If there is no such entry and no default value as been
|
||||||
|
* specified.
|
||||||
|
*/
|
||||||
|
function get($path, $default = 'throws an exception')
|
||||||
|
{
|
||||||
|
$entry = $this->_entries;
|
||||||
|
|
||||||
|
$parts = explode('.', $path);
|
||||||
|
foreach ($parts as $part)
|
||||||
|
{
|
||||||
|
/*
|
||||||
|
* Nothing found.
|
||||||
|
*/
|
||||||
|
if (!isset($entry[$part])
|
||||||
|
&& !array_key_exists($part, $entry))
|
||||||
|
{
|
||||||
|
if (func_num_args() < 2)
|
||||||
|
{
|
||||||
|
throw new Exception('no such entry ('.$path.')');
|
||||||
|
}
|
||||||
|
|
||||||
|
$entry = $default;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
$entry = $entry[$part];
|
||||||
|
}
|
||||||
|
|
||||||
|
return $this->_resolve($entry);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
*
|
||||||
|
* @param string $path
|
||||||
|
* @param array|string $value
|
||||||
|
*/
|
||||||
|
function set($path, $value)
|
||||||
|
{
|
||||||
|
$entry = &$this->_entries;
|
||||||
|
|
||||||
|
$parts = explode('.', $path);
|
||||||
|
|
||||||
|
$i = 0;
|
||||||
|
$n = count($parts);
|
||||||
|
while (
|
||||||
|
($i < $n)
|
||||||
|
&& (
|
||||||
|
isset($entry[$part = $parts[$i]])
|
||||||
|
|| (
|
||||||
|
is_array($entry)
|
||||||
|
&& array_key_exists($part, $entry)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
{
|
||||||
|
$entry = &$entry[$part];
|
||||||
|
++$i;
|
||||||
|
}
|
||||||
|
|
||||||
|
while ($i < $n)
|
||||||
|
{
|
||||||
|
if (!is_array($entry))
|
||||||
|
{
|
||||||
|
$entry = array();
|
||||||
|
}
|
||||||
|
|
||||||
|
$entry = &$entry[$parts[$i]];
|
||||||
|
++$i;
|
||||||
|
}
|
||||||
|
|
||||||
|
$entry = $value;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var array
|
||||||
|
*/
|
||||||
|
private $_entries;
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
private function _replaceCallback(array $match)
|
||||||
|
{
|
||||||
|
return $this->get($match[1]);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
private function _resolve($entry)
|
||||||
|
{
|
||||||
|
if (is_string($entry))
|
||||||
|
{
|
||||||
|
return preg_replace_callback(
|
||||||
|
'/#\{([-a-zA-Z0-9_.]+)\}/',
|
||||||
|
array($this, '_replaceCallback'),
|
||||||
|
$entry
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (is_array($entry))
|
||||||
|
{
|
||||||
|
foreach ($entry as &$item)
|
||||||
|
{
|
||||||
|
$item = $this->_resolve($item);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return $entry;
|
||||||
|
}
|
||||||
|
}
|
83
src/lib/Connection.php
Normal file
83
src/lib/Connection.php
Normal file
@ -0,0 +1,83 @@
|
|||||||
|
<?php
|
||||||
|
/**
|
||||||
|
* This file is a part of Xen Orchestra Server.
|
||||||
|
*
|
||||||
|
* Xen Orchestra Server 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 3 of
|
||||||
|
* the License, or (at your option) any later version.
|
||||||
|
*
|
||||||
|
* Xen Orchestra Server 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.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with Xen Orchestra Server. If not, see
|
||||||
|
* <http://www.gnu.org/licenses/>.
|
||||||
|
*
|
||||||
|
* @author Julien Fontanet <julien.fontanet@vates.fr>
|
||||||
|
* @license http://www.gnu.org/licenses/gpl-3.0-standalone.html GPLv3
|
||||||
|
*
|
||||||
|
* @package Xen Orchestra Server
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
abstract class Connection extends Base
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
function __construct($handle, Loop $loop)
|
||||||
|
{
|
||||||
|
parent::__construct();
|
||||||
|
|
||||||
|
$this->_handle = $handle;
|
||||||
|
$this->_loop = $loop;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
function __destruct()
|
||||||
|
{
|
||||||
|
$this->close();
|
||||||
|
|
||||||
|
parent::__destruct();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
final function close()
|
||||||
|
{
|
||||||
|
$this->_loop->removeRead($this->_handle);
|
||||||
|
$this->_loop->removeWrite($this->_handle);
|
||||||
|
|
||||||
|
$this->handleClose();
|
||||||
|
|
||||||
|
if (is_resource($this->_handle))
|
||||||
|
{
|
||||||
|
stream_socket_shutdown($this->_handle, STREAM_SHUT_RDWR);
|
||||||
|
fclose($this->_handle);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
function handleClose()
|
||||||
|
{}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var resource
|
||||||
|
*/
|
||||||
|
private $_handle;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var Loop
|
||||||
|
*/
|
||||||
|
private $_loop;
|
||||||
|
}
|
112
src/lib/DI.php
Normal file
112
src/lib/DI.php
Normal file
@ -0,0 +1,112 @@
|
|||||||
|
<?php
|
||||||
|
/**
|
||||||
|
* This file is a part of Xen Orchestra Server.
|
||||||
|
*
|
||||||
|
* Xen Orchestra Server 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 3 of
|
||||||
|
* the License, or (at your option) any later version.
|
||||||
|
*
|
||||||
|
* Xen Orchestra Server 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.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with Xen Orchestra Server. If not, see
|
||||||
|
* <http://www.gnu.org/licenses/>.
|
||||||
|
*
|
||||||
|
* @author Julien Fontanet <julien.fontanet@vates.fr>
|
||||||
|
* @license http://www.gnu.org/licenses/gpl-3.0-standalone.html GPLv3
|
||||||
|
*
|
||||||
|
* @package Xen Orchestra Server
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Dependency injector.
|
||||||
|
*/
|
||||||
|
final class DI extends Base
|
||||||
|
{
|
||||||
|
function __construct()
|
||||||
|
{
|
||||||
|
parent::__construct();
|
||||||
|
}
|
||||||
|
|
||||||
|
function get($id)
|
||||||
|
{
|
||||||
|
if (isset($this->_entries[$id])
|
||||||
|
|| array_key_exists($id, $this->_entries))
|
||||||
|
{
|
||||||
|
return $this->_entries[$id];
|
||||||
|
}
|
||||||
|
|
||||||
|
$tmp = str_replace(array('_', '.'), array('', '_'), $id);
|
||||||
|
|
||||||
|
if (method_exists($this, '_get_'.$tmp))
|
||||||
|
{
|
||||||
|
return $this->{'_get_'.$tmp}();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (method_exists($this, '_init_'.$tmp))
|
||||||
|
{
|
||||||
|
$value = $this->{'_init_'.$tmp}();
|
||||||
|
$this->set($id, $value);
|
||||||
|
return $value;
|
||||||
|
}
|
||||||
|
|
||||||
|
throw new Exception('no such entry: '.$id);
|
||||||
|
}
|
||||||
|
|
||||||
|
function set($id, $value)
|
||||||
|
{
|
||||||
|
$this->_entries[$id] = $value;
|
||||||
|
}
|
||||||
|
|
||||||
|
private $_entries = array();
|
||||||
|
|
||||||
|
////////////////////////////////////////
|
||||||
|
|
||||||
|
private function _init_application()
|
||||||
|
{
|
||||||
|
return new Application($this);
|
||||||
|
}
|
||||||
|
|
||||||
|
private function _init_errorLogger()
|
||||||
|
{
|
||||||
|
return new ErrorLogger($this->get('logger'));
|
||||||
|
}
|
||||||
|
|
||||||
|
private function _init_logger()
|
||||||
|
{
|
||||||
|
$logger = new \Monolog\Logger('main');
|
||||||
|
|
||||||
|
$config = $this->get('config');
|
||||||
|
if ($email = $config->get('log.email', false))
|
||||||
|
{
|
||||||
|
$logger->pushHandler(
|
||||||
|
new \Monolog\Handler\FingersCrossedHandler(
|
||||||
|
new \Monolog\Handler\NativeMailerHandler(
|
||||||
|
$email,
|
||||||
|
'[XO Server]',
|
||||||
|
'no-reply@vates.fr',
|
||||||
|
\Monolog\Logger::DEBUG
|
||||||
|
),
|
||||||
|
\Monolog\Logger::WARNING
|
||||||
|
)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
if ($file = $config->get('log.file', false))
|
||||||
|
{
|
||||||
|
$logger->pushHandler(
|
||||||
|
new \Monolog\Handler\StreamHandler($file)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
return $logger;
|
||||||
|
}
|
||||||
|
|
||||||
|
private function _init_loop()
|
||||||
|
{
|
||||||
|
return new Loop;
|
||||||
|
}
|
||||||
|
}
|
110
src/lib/ErrorLogger.php
Normal file
110
src/lib/ErrorLogger.php
Normal file
@ -0,0 +1,110 @@
|
|||||||
|
<?php
|
||||||
|
/**
|
||||||
|
* This file is a part of Xen Orchestra Server.
|
||||||
|
*
|
||||||
|
* Xen Orchestra Server 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 3 of
|
||||||
|
* the License, or (at your option) any later version.
|
||||||
|
*
|
||||||
|
* Xen Orchestra Server 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.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with Xen Orchestra Server. If not, see
|
||||||
|
* <http://www.gnu.org/licenses/>.
|
||||||
|
*
|
||||||
|
* @author Julien Fontanet <julien.fontanet@vates.fr>
|
||||||
|
* @license http://www.gnu.org/licenses/gpl-3.0-standalone.html GPLv3
|
||||||
|
*
|
||||||
|
* @package Xen Orchestra Server
|
||||||
|
*/
|
||||||
|
|
||||||
|
use \Monolog\Logger;
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
final class ErrorLogger extends Base
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
function __construct(Logger $logger)
|
||||||
|
{
|
||||||
|
parent::__construct();
|
||||||
|
|
||||||
|
$this->_logger = $logger;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Handles fatal errors on shutdown.
|
||||||
|
*/
|
||||||
|
function handleShutdown()
|
||||||
|
{
|
||||||
|
$e = error_get_Last();
|
||||||
|
if ((($e['type'] === E_ERROR) || ($e['type'] === E_USER_ERROR))
|
||||||
|
&& ($e !== $this->_last))
|
||||||
|
{
|
||||||
|
$this->log($e['type'], $e['message'], $e['file'], $e['line']);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
function log($no, $str, $file, $line)
|
||||||
|
{
|
||||||
|
static $map = array(
|
||||||
|
E_NOTICE => Logger::NOTICE,
|
||||||
|
E_USER_NOTICE => Logger::NOTICE,
|
||||||
|
E_WARNING => Logger::WARNING,
|
||||||
|
E_CORE_WARNING => Logger::WARNING,
|
||||||
|
E_USER_WARNING => Logger::WARNING,
|
||||||
|
E_ERROR => Logger::ERROR,
|
||||||
|
E_USER_ERROR => Logger::ERROR,
|
||||||
|
E_CORE_ERROR => Logger::ERROR,
|
||||||
|
E_RECOVERABLE_ERROR => Logger::ERROR,
|
||||||
|
E_STRICT => Logger::DEBUG,
|
||||||
|
);
|
||||||
|
|
||||||
|
// Used to prevents the last error from being logged twice.
|
||||||
|
$this->_last = array(
|
||||||
|
'type' => $no,
|
||||||
|
'message' => $str,
|
||||||
|
'file' => $file,
|
||||||
|
'line' => $line
|
||||||
|
);
|
||||||
|
|
||||||
|
$priority = isset($map[$no])
|
||||||
|
? $map[$no]
|
||||||
|
: Logger::WARNING
|
||||||
|
;
|
||||||
|
|
||||||
|
// Appends the location if necessary.
|
||||||
|
if (!preg_match('/(?:at|in) [^ ]+:[0-9]+$/', $str))
|
||||||
|
{
|
||||||
|
$str .= " in $file:$line";
|
||||||
|
}
|
||||||
|
|
||||||
|
$this->_logger->addRecord($priority, $str, array(
|
||||||
|
'no' => $no,
|
||||||
|
'file' => $file,
|
||||||
|
'line' => $line,
|
||||||
|
));
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
private $_last;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var Logger
|
||||||
|
*/
|
||||||
|
private $_logger;
|
||||||
|
}
|
176
src/lib/Loop.php
Normal file
176
src/lib/Loop.php
Normal file
@ -0,0 +1,176 @@
|
|||||||
|
<?php
|
||||||
|
/**
|
||||||
|
* This file is a part of Xen Orchestra Server.
|
||||||
|
*
|
||||||
|
* Xen Orchestra Server 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 3 of
|
||||||
|
* the License, or (at your option) any later version.
|
||||||
|
*
|
||||||
|
* Xen Orchestra Server 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.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with Xen Orchestra Server. If not, see
|
||||||
|
* <http://www.gnu.org/licenses/>.
|
||||||
|
*
|
||||||
|
* @author Julien Fontanet <julien.fontanet@vates.fr>
|
||||||
|
* @license http://www.gnu.org/licenses/gpl-3.0-standalone.html GPLv3
|
||||||
|
*
|
||||||
|
* @package Xen Orchestra Server
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
final class Loop extends Base
|
||||||
|
{
|
||||||
|
function __construct()
|
||||||
|
{
|
||||||
|
parent::__construct();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param resource $handle
|
||||||
|
* @param callable $callback
|
||||||
|
*/
|
||||||
|
function addRead($handle, $callback)
|
||||||
|
{
|
||||||
|
$id = (int) $handle;
|
||||||
|
|
||||||
|
$this->_readHandles[$id] = $handle;
|
||||||
|
$this->_readCallbacks[$id] = $callback;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
function removeRead($handle)
|
||||||
|
{
|
||||||
|
$id = (int) $handle;
|
||||||
|
|
||||||
|
unset(
|
||||||
|
$this->_readHandles[$id],
|
||||||
|
$this->_readCallbacks[$id]
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param resource $handle
|
||||||
|
* @param callable $callback
|
||||||
|
*/
|
||||||
|
function addWrite($handle, $callback)
|
||||||
|
{
|
||||||
|
$id = (int) $handle;
|
||||||
|
|
||||||
|
$this->_writeHandles[$id] = $handle;
|
||||||
|
$this->_writeCallbacks[$id] = $callback;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
function removeWrite($handle)
|
||||||
|
{
|
||||||
|
$id = (int) $handle;
|
||||||
|
|
||||||
|
unset(
|
||||||
|
$this->_writeHandles[$id],
|
||||||
|
$this->_writeCallbacks[$id]
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
function remove($handle)
|
||||||
|
{
|
||||||
|
$id = (int) $handle;
|
||||||
|
|
||||||
|
unset(
|
||||||
|
$this->_readHandles[$id],
|
||||||
|
$this->_readCallbacks[$id],
|
||||||
|
$this->_writeHandles[$id],
|
||||||
|
$this->_writeCallbacks[$id]
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param mixed $user_data
|
||||||
|
*/
|
||||||
|
function run($user_data = null)
|
||||||
|
{
|
||||||
|
$this->_running = true;
|
||||||
|
|
||||||
|
do
|
||||||
|
{
|
||||||
|
$read = $this->_readHandles;
|
||||||
|
$write = $this->_writeHandles;
|
||||||
|
$except = null;
|
||||||
|
if (@stream_select($read, $write, $except, null) === false)
|
||||||
|
{
|
||||||
|
trigger_error(
|
||||||
|
'error while waiting for activity',
|
||||||
|
E_USER_ERROR
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach ($read as $handle)
|
||||||
|
{
|
||||||
|
$callback = $this->_readCallbacks[(int) $handle];
|
||||||
|
$result = $callback($handle, $user_data);
|
||||||
|
|
||||||
|
if (!is_resource($handle))
|
||||||
|
{
|
||||||
|
$this->remove($handle);
|
||||||
|
}
|
||||||
|
elseif ($result === false)
|
||||||
|
{
|
||||||
|
$this->removeRead($handle);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
foreach ($write as $handle)
|
||||||
|
{
|
||||||
|
$callback = $this->_writeCallbacks[(int) $handle];
|
||||||
|
$result = $callback($handle, $user_data);
|
||||||
|
|
||||||
|
if (!is_resource($handle))
|
||||||
|
{
|
||||||
|
$this->remove($handle);
|
||||||
|
}
|
||||||
|
elseif ($result === false)
|
||||||
|
{
|
||||||
|
$this->removeWrite($handle);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} while ($this->_running
|
||||||
|
&& ($this->_readHandles || $this->_writeHandles));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var boolean
|
||||||
|
*/
|
||||||
|
private $_running;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var resource[]
|
||||||
|
*/
|
||||||
|
private $_readHandles = array();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var callable[]
|
||||||
|
*/
|
||||||
|
private $_readCallbacks = array();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var resource[]
|
||||||
|
*/
|
||||||
|
private $_writeHandles = array();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var callable[]
|
||||||
|
*/
|
||||||
|
private $_writeCallbacks = array();
|
||||||
|
}
|
198
src/lib/XCP.php
Normal file
198
src/lib/XCP.php
Normal file
@ -0,0 +1,198 @@
|
|||||||
|
<?php
|
||||||
|
/**
|
||||||
|
* This file is a part of Xen Orchestra Server.
|
||||||
|
*
|
||||||
|
* Xen Orchestra Server 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 3 of
|
||||||
|
* the License, or (at your option) any later version.
|
||||||
|
*
|
||||||
|
* Xen Orchestra Server 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.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with Xen Orchestra Server. If not, see
|
||||||
|
* <http://www.gnu.org/licenses/>.
|
||||||
|
*
|
||||||
|
* @author Julien Fontanet <julien.fontanet@vates.fr>
|
||||||
|
* @license http://www.gnu.org/licenses/gpl-3.0-standalone.html GPLv3
|
||||||
|
*
|
||||||
|
* @package Xen Orchestra Server
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
final class XCP extends Base
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
function __construct(Loop $loop, $url, $user, $password)
|
||||||
|
{
|
||||||
|
parent::__construct();
|
||||||
|
|
||||||
|
$this->_loop = $loop;
|
||||||
|
$this->_url = $url;
|
||||||
|
|
||||||
|
$this->_queue[] = array(
|
||||||
|
array($this, '_setToken'),
|
||||||
|
'session.login_with_password',
|
||||||
|
array($user, $password)
|
||||||
|
);
|
||||||
|
$this->_next();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
function queue($method, array $params = null, $callback = null)
|
||||||
|
{
|
||||||
|
($params !== null)
|
||||||
|
or $params = array();
|
||||||
|
|
||||||
|
$this->_queue[] = array($callback, $method, $params);
|
||||||
|
|
||||||
|
if (count($this->_queue) === 1)
|
||||||
|
{
|
||||||
|
$this->_next();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
function onData($handle)
|
||||||
|
{
|
||||||
|
$this->_buf .= stream_get_contents($handle);
|
||||||
|
|
||||||
|
if (!feof($handle))
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Reads the response.
|
||||||
|
list(, $response) = explode("\r\n\r\n", $this->_buf, 2);
|
||||||
|
$this->_buf = '';
|
||||||
|
$response = xmlrpc_decode($response);
|
||||||
|
if (!isset($response['Value']))
|
||||||
|
{
|
||||||
|
var_export($response);
|
||||||
|
trigger_error(
|
||||||
|
'Invalid response',
|
||||||
|
E_USER_ERROR
|
||||||
|
);
|
||||||
|
}
|
||||||
|
$response = $response['Value'];
|
||||||
|
|
||||||
|
// Notifies.
|
||||||
|
$request = array_shift($this->_queue);
|
||||||
|
if ($request[0] !== null)
|
||||||
|
{
|
||||||
|
$callback = $request[0];
|
||||||
|
$result = $callback($response, $this);
|
||||||
|
if ($result === true)
|
||||||
|
{
|
||||||
|
$this->_queue[] = $request;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Sends the next request if any.
|
||||||
|
if ($this->_queue)
|
||||||
|
{
|
||||||
|
$this->_next();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Stops reading this socket.
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
private function _next()
|
||||||
|
{
|
||||||
|
$_ = parse_url($this->_url);
|
||||||
|
$https = isset($_['scheme']) && !strcasecmp($_['scheme'], 'https');
|
||||||
|
$port = isset($_['port']) ? $_['port'] : ($https ? 443 : 80);
|
||||||
|
$host = isset($_['host']) ? $_['host'] : '127.0.0.1';
|
||||||
|
$query = isset($_['query']) ? '?'.$_['query'] : '';
|
||||||
|
$path = isset($_['path']) ? $_['path'] : '/';
|
||||||
|
|
||||||
|
list(, $method, $params) = reset($this->_queue);
|
||||||
|
isset($this->_tok)
|
||||||
|
and array_unshift($params, $this->_tok);
|
||||||
|
|
||||||
|
$data = xmlrpc_encode_request($method, $params, array(
|
||||||
|
'verbosity' => 'no_white_space',
|
||||||
|
'encoding' => 'UTF-8',
|
||||||
|
));
|
||||||
|
|
||||||
|
$request = implode("\r\n", array(
|
||||||
|
"POST $path HTTP/1.1",
|
||||||
|
"Host: $host:$port",
|
||||||
|
'Connection: close',
|
||||||
|
'Content-Type: text/xml; charset=UTF-8',
|
||||||
|
'Content-Length: '.strlen($data),
|
||||||
|
'',
|
||||||
|
$data
|
||||||
|
));
|
||||||
|
|
||||||
|
$hdl = @stream_socket_client(
|
||||||
|
($https ? 'tls' : 'tcp')."://$host:$port",
|
||||||
|
/* out */ $errno,
|
||||||
|
/* out */ $errstr,
|
||||||
|
ini_get('default_socket_timeout'), // Default value.
|
||||||
|
STREAM_CLIENT_CONNECT | STREAM_CLIENT_ASYNC_CONNECT
|
||||||
|
);
|
||||||
|
if (!$hdl)
|
||||||
|
{
|
||||||
|
trigger_error(
|
||||||
|
"cannot connect to {$this->_url}: $errno - $errstr",
|
||||||
|
E_USER_ERROR
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
stream_set_blocking($hdl, false);
|
||||||
|
fwrite($hdl, $request);
|
||||||
|
|
||||||
|
$this->_loop->addRead($hdl, array($this, 'onData'));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
private function _setToken($token)
|
||||||
|
{
|
||||||
|
$this->_tok = $token;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var string
|
||||||
|
*/
|
||||||
|
private $_buf = '';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var Loop
|
||||||
|
*/
|
||||||
|
private $_loop;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var string
|
||||||
|
*/
|
||||||
|
private $_url;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Session token.
|
||||||
|
*
|
||||||
|
* @var string
|
||||||
|
*/
|
||||||
|
private $_tok;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var array
|
||||||
|
*/
|
||||||
|
private $_queue = array();
|
||||||
|
}
|
73
src/xo-server
Executable file
73
src/xo-server
Executable file
@ -0,0 +1,73 @@
|
|||||||
|
#!/usr/bin/php
|
||||||
|
<?php
|
||||||
|
/**
|
||||||
|
* This file is a part of Xen Orchestra Server.
|
||||||
|
*
|
||||||
|
* Xen Orchestra Server 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 3 of
|
||||||
|
* the License, or (at your option) any later version.
|
||||||
|
*
|
||||||
|
* Xen Orchestra Server 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.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with Xen Orchestra Server. If not, see
|
||||||
|
* <http://www.gnu.org/licenses/>.
|
||||||
|
*
|
||||||
|
* @author Julien Fontanet <julien.fontanet@vates.fr>
|
||||||
|
* @license http://www.gnu.org/licenses/gpl-3.0-standalone.html GPLv3
|
||||||
|
*
|
||||||
|
* @package Xen Orchestra Server
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Bootstraps and returns the application singleton.
|
||||||
|
*/
|
||||||
|
function _bootstrap()
|
||||||
|
{
|
||||||
|
static $application;
|
||||||
|
|
||||||
|
if (!isset($application))
|
||||||
|
{
|
||||||
|
// Variables definition.
|
||||||
|
$root_dir = defined('__DIR__')
|
||||||
|
? __DIR__
|
||||||
|
: dirname(__FILE__)
|
||||||
|
;
|
||||||
|
|
||||||
|
// Class autoloading is done by composer.
|
||||||
|
require($root_dir.'/../vendor/autoload.php');
|
||||||
|
|
||||||
|
// Reads configuration.
|
||||||
|
$config = new Config(array_merge_recursive(
|
||||||
|
require($root_dir.'/config/global.php'),
|
||||||
|
require($root_dir.'/config/local.php')
|
||||||
|
));
|
||||||
|
|
||||||
|
// Injects some variables.
|
||||||
|
$config->set('root_dir', $root_dir);
|
||||||
|
|
||||||
|
// Dependency injector.
|
||||||
|
$di = new DI;
|
||||||
|
$di->set('config', $config);
|
||||||
|
|
||||||
|
// Logs all errors.
|
||||||
|
$error_logger = $di->get('error_logger');
|
||||||
|
set_error_handler(array($error_logger, 'log'));
|
||||||
|
register_shutdown_function(array($error_logger, 'handleShutdown'));
|
||||||
|
|
||||||
|
// Finally, creates the application.
|
||||||
|
$application = $di->get('application');
|
||||||
|
}
|
||||||
|
|
||||||
|
return $application;
|
||||||
|
}
|
||||||
|
|
||||||
|
_bootstrap()->run();
|
||||||
|
|
||||||
|
// Local Variables:
|
||||||
|
// mode: php
|
||||||
|
// End:
|
Loading…
x
Reference in New Issue
Block a user