Viewing File: /usr/local/cpanel/3rdparty/wpinstaller/src/WpInstaller/CpanelApi.php
<?php
namespace WpInstaller;
use \WpInstaller\Database\ApiInterface;
class CpanelApi implements \WpInstaller\Database\ApiInterface {
private $username;
private $interface;
public $fake_cpanel_api;
function __construct() {
$this->fake_cpanel_api = (
array_key_exists('FAKE_CPANEL_API', $_ENV)
&& $_ENV['FAKE_CPANEL_API']
);
$get_type = function () {
if ($this->fake_cpanel_api) {
return 'fake';
}
$live_api_available = (
array_key_exists('CPANEL_PHPCONNECT_SOCKET', $_ENV)
&& $_ENV['CPANEL_PHPCONNECT_SOCKET']
);
if ($live_api_available) {
return 'liveapi';
}
return 'cli';
};
$this->interface = [
'liveapi' => function () { return ( new LiveAPI() ); },
'cli' => function () { return ( new Cli() ); },
'fake' => function () { return ( new DockerEnvFakeAPI() ); },
][ $get_type() ]();
}
public function create_database(string $database_name) {
return $this->call(
'Mysql', 'create_database',
['name' => $database_name]
);
}
public function delete_database(string $database_name) {
return $this->call(
'Mysql', 'delete_database',
['name' => $database_name]
);
}
public function create_user(string $username, string $password) {
return $this->call(
'Mysql', 'create_user',
[
'name' => $username,
'password' => $password,
]
);
}
public function delete_user(string $username) {
return $this->call(
'Mysql', 'delete_user',
['name' => $username]
);
}
public function set_user_privs(string $username, string $database_name, string $privileges = 'ALL') {
return $this->call(
'Mysql', 'set_privileges_on_database',
[
'user' => $username,
'database' => $database_name,
'privileges' => $privileges,
]
);
}
public function get_server_host() {
$resp = $this->call('Mysql', 'get_server_information', []);
return $resp->data->host;
}
private function call($module, $function, $args) {
$resp = ($this->interface)($module, $function, $args);
$resp = json_decode($resp);
if ( property_exists($resp, 'module') && property_exists($resp, 'result') ) {
// nested cPanel Uapi output from cli
$resp = $resp->result;
}
if ($resp->status !== 1) {
$details = print_r($resp, true);
throw new \Exception("Error calling cPanelAPI: $details");
}
return $resp;
}
}
/*
Generates a CLI command that looks something like:
uapi Mysql get_privileges_on_database user=wpinstal_WPAG database=wpinstal_WPAG
and executes it returning the resulting output.
*/
class Cli {
function __invoke(string $module, string $method, $arguments=[]) {
$mod = escapeshellarg($module);
$meth = escapeshellarg($method);
$command = ['uapi', $mod, $meth];
foreach ($arguments as $key => $value) {
$k = escapeshellarg($key);
$v = escapeshellarg($value);
$command[] = "{$k}={$v}";
}
$command[] = '--output=json';
$command = implode(' ', $command);
return shell_exec($command);
}
}
// https://documentation.cpanel.net/display/DD/Guide+to+the+LiveAPI+System+-+PHP+Class
class LiveAPI {
const CPANEL_PHP_PATH = '/usr/local/cpanel/php/cpanel.php';
private $cpanel;
function __construct() {
require_once CPANEL_PHP_PATH;
$this->cpanel = new CPANEL();
}
function __invoke(string $module, string $method, $arguments=[]) {
return $this->cpanel()->uapi($module, $func, $args);
}
}
/* DockerEnvFakeAPI
A fake api that allows for making the necessariy calls using raw sql on a mysql connection.
This is meant to be used primarily for testing in the docker development environment.
*/
class DockerEnvFakeAPI {
const DOCKER_TEST_HOST = 'wp-installer_mysql';
const DOCKER_TEST_USER = 'root';
const DOCKER_TEST_PW = 'super-duper-Secret-42';
const SEC_TO_WAIT_FOR_DBSERVER = 15;
function __construct() {
$wait_for = self::SEC_TO_WAIT_FOR_DBSERVER;
$waited = 0;
while($waited <= $wait_for) {
sleep(1);
try {
// Test database server credentials
$this->mysqli = mysqli_connect(
self::DOCKER_TEST_HOST,
'root',
'super-duper-Secret-42'
);
} catch (\Exception $e) {
$remaining = $wait_for - $waited;
echo "Waiting $remaining more seconds for db server". PHP_EOL;
}
if(!!$this->mysqli) {
break;
}
$waited++;
}
if(mysqli_connect_error()) {
throw new \Exception ("Failed to connect to Mysql:" . mysqli_connect_error());
}
}
function __invoke(string $module, string $method, $arguments=[]) {
$prefix = strtolower($module). '_';
$method_name = "{$prefix}{$method}";
if(!method_exists($this, $method_name)) {
throw new \Exception("$module $method not implemented in ".get_class($this)." $method_name");
}
$response = [
'messages' => null,
'errors' => null,
'data' => null,
'status' => 1,
];
try {
$response['data'] = $this->$method_name($arguments);
}
catch (Exception $e) {
$response['errors'] = $e;
$response['status'] = 0;
}
return json_encode($response);
}
private function mysql_create_user($args) {
$this->require_args(['name', 'password'], $args);
# Not binding params because that's not allowed in "CREATE USER" statements.
# We're okay with this because this is a FAKE used for testing only.
$this->query("CREATE USER '{$args['name']}'@'%' IDENTIFIED BY '{$args['password']}'");
}
private function mysql_create_database($args) {
$this->require_args(['name'], $args);
$this->query("CREATE DATABASE {$args['name']}");
}
private function mysql_set_privileges_on_database($args) {
$this->require_args(['user', 'database', 'privileges'], $args);
$this->query( "GRANT {$args['privileges']} ON {$args['database']}.* TO '{$args['user']}'@'%'");
$this->query('FLUSH PRIVILEGES');
}
private function mysql_delete_user($args) {
$this->require_args(['name'], $args);
$this->query("DROP USER {$args['name']}");
}
private function mysql_delete_database($args) {
$this->require_args(['name'], $args);
$this->query("DROP DATABASE {$args['name']}");
}
private function mysql_get_server_information($args) {
return [ 'host' => self::DOCKER_TEST_HOST ];
}
private function require_args($required, $given) {
foreach ($required as $e_arg) {
if (!array_key_exists($e_arg, $given)) {
throw new \Exception("Missing required argument $e_arg");
}
}
}
private function query($query, $bind_types = '', $params = []) {
$statement = $this->mysqli->prepare($query);
if (!$statement) {
throw new \Exception("Failed to prepare query: {$this->mysqli->error}");
}
if (!empty($bind_types) && !$statement->bind_param($bind_types, $params)) {
throw new \Exception("Failed to bind params: {$statement->error}");
}
if (!$statement->execute()) {
throw new \Exception("Failed to execute query: {$statement->error}");
}
}
function __destruct() {
$this->mysqli->close();
}
}
?>
Back to Directory
File Manager