mirror of
https://github.com/mclueppers/xo-server.git
synced 2025-04-08 20:55:02 +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