| Current Path : /home/s/u/n/sunflowe/www2/vacancesfloride/components/com_akeeba/models/ |
| Current File : /home/s/u/n/sunflowe/www2/vacancesfloride/components/com_akeeba/models/jsons.php |
<?php
/**
* @package AkeebaBackup
* @copyright Copyright (c)2009-2016 Nicholas K. Dionysopoulos
* @license GNU General Public License version 3, or later
*
* @since 3.0
*/
// Protect from unauthorized access
defined('_JEXEC') or die();
// JSON API version number
define('AKEEBA_JSON_API_VERSION', '320');
use Akeeba\Engine\Factory;
use Akeeba\Engine\Platform;
use Akeeba\Engine\Util\Encrypt;
/*
* Short API version history:
* 300 First draft. Basic backup working. Encryption semi-broken.
* 316 Fixed download feature.
*/
if (!defined('AKEEBA_BACKUP_ORIGIN'))
{
define('AKEEBA_BACKUP_ORIGIN', 'json');
}
class AkeebaModelJsons extends F0FModel
{
const COM_AKEEBA_CPANEL_LBL_STATUS_OK = 200; // Normal reply
const STATUS_NOT_AUTH = 401; // Invalid credentials
const STATUS_NOT_ALLOWED = 403; // Not enough privileges
const STATUS_NOT_FOUND = 404; // Requested resource not found
const STATUS_INVALID_METHOD = 405; // Unknown JSON method
const COM_AKEEBA_CPANEL_LBL_STATUS_ERROR = 500; // An error occurred
const STATUS_NOT_IMPLEMENTED = 501; // Not implemented feature
const STATUS_NOT_AVAILABLE = 503; // Remote service not activated
const ENCAPSULATION_RAW = 1; // Data in plain-text JSON
const ENCAPSULATION_AESCTR128 = 2; // Data in AES-128 stream (CTR) mode encrypted JSON
const ENCAPSULATION_AESCTR256 = 3; // Data in AES-256 stream (CTR) mode encrypted JSON
const ENCAPSULATION_AESCBC128 = 4; // Data in AES-128 standard (CBC) mode encrypted JSON
const ENCAPSULATION_AESCBC256 = 5; // Data in AES-256 standard (CBC) mode encrypted JSON
/** @var int The status code */
private $status = 200;
/** @var int Data encapsulation format */
private $encapsulation = 1;
/** @var mixed Any data to be returned to the caller */
private $data = '';
/** @var string A password passed to us by the caller */
private $password = null;
/** @var string The method called by the client */
private $method_name = null;
public function execute($json)
{
// Check if we're activated
$enabled = Platform::getInstance()->get_platform_configuration_option('frontend_enable', 0);
// Is the Secret Key strong enough?
$validKey = $this->serverKey();
if (!\Akeeba\Engine\Util\Complexify::isStrongEnough($validKey, false))
{
$enabled = false;
}
if (!$enabled)
{
$this->data = 'Access denied';
$this->status = self::STATUS_NOT_AVAILABLE;
$this->encapsulation = self::ENCAPSULATION_RAW;
return $this->getResponse();
}
// Try to JSON-decode the request's input first
$request = @json_decode($json, false);
if (is_null($request))
{
// Could not decode JSON
$this->data = 'JSON decoding error';
$this->status = self::COM_AKEEBA_CPANEL_LBL_STATUS_ERROR;
$this->encapsulation = self::ENCAPSULATION_RAW;
return $this->getResponse();
}
// Decode the request body
// Request format: {encapsulation, body{ [key], [challenge], method, [data] }} or {[challenge], method, [data]}
if (isset($request->encapsulation) && isset($request->body))
{
if (!class_exists('\\Akeeba\\Engine\\Util\\Encrypt') && !($request->encapsulation == self::ENCAPSULATION_RAW))
{
// Encrypted request found, but there is no encryption class available!
$this->data = 'This server does not support encrypted requests';
$this->status = self::STATUS_NOT_AVAILABLE;
$this->encapsulation = self::ENCAPSULATION_RAW;
return $this->getResponse();
}
// Fully specified request
$body = '';
switch ($request->encapsulation)
{
case self::ENCAPSULATION_AESCBC128:
if (!isset($body))
{
$request->body = base64_decode($request->body);
$body = Factory::getEncryption()
->AESDecryptCBC($request->body, $this->serverKey(), 128);
}
break;
case self::ENCAPSULATION_AESCBC256:
if (!isset($body))
{
$request->body = base64_decode($request->body);
$body = Factory::getEncryption()
->AESDecryptCBC($request->body, $this->serverKey(), 256);
}
break;
case self::ENCAPSULATION_AESCTR128:
if (!isset($body))
{
$body = Factory::getEncryption()->AESDecryptCtr($request->body, $this->serverKey(), 128);
}
break;
case self::ENCAPSULATION_AESCTR256:
if (!isset($body))
{
$body = Factory::getEncryption()->AESDecryptCtr($request->body, $this->serverKey(), 256);
}
break;
case self::ENCAPSULATION_RAW:
$body = $request->body;
break;
}
if (!empty($request->body))
{
$authorised = true;
$body = rtrim($body, chr(0));
// Make sure it looks like a valid JSON string and is at least 12 characters (minimum valid message length)
if ((strlen($body) < 12) || (substr($body, 0, 1) != '{') || (substr($body, - 1) != '}'))
{
$authorised = false;
}
// Try to JSON decode the body
if ($authorised)
{
$request->body = json_decode($body);
if (is_null($request->body))
{
$authorised = false;
}
elseif (!is_object($request->body))
{
$authorised = false;
}
}
// Make sure there is a requested method
if ($authorised)
{
if (!isset($request->body->method) || empty($request->body->method))
{
$authorised = false;
}
}
if (!$authorised)
{
// Decryption failed. The user is an impostor! Go away, hacker!
$this->data = 'Authentication failed';
$this->status = self::STATUS_NOT_AUTH;
$this->encapsulation = self::ENCAPSULATION_RAW;
return $this->getResponse();
}
}
}
elseif (isset($request->body))
{
// Partially specified request, assume RAW encapsulation
$request->encapsulation = self::ENCAPSULATION_RAW;
$request->body = json_decode($request->body);
}
else
{
// Legacy request
$legacyRequest = clone $request;
$request = (object) array('encapsulation' => self::ENCAPSULATION_RAW, 'body' => null);
$request->body = json_decode($legacyRequest);
unset($legacyRequest);
}
// Authenticate the user. Do note that if an encrypted request was made, we can safely assume that
// the user is authenticated (he already knows the server key!)
if ($request->encapsulation == self::ENCAPSULATION_RAW)
{
$authenticated = false;
if (isset($request->body->challenge))
{
list($challenge, $check) = explode(':', $request->body->challenge);
$crosscheck = strtolower(md5($challenge . $this->serverKey()));
$authenticated = ($crosscheck == $check);
}
if (!$authenticated)
{
// If the challenge was missing or it was wrong, don't let him go any further
$this->data = 'Invalid login credentials';
$this->status = self::STATUS_NOT_AUTH;
$this->encapsulation = self::ENCAPSULATION_RAW;
return $this->getResponse();
}
}
// Replicate the encapsulation preferences of the client for our own output
$this->encapsulation = $request->encapsulation;
// Store the client-specified key, or use the server key if none specified and the request
// came encrypted.
$this->password = isset($request->body->key) ? $request->body->key : null;
$hasKey = (isset($request->body->key) || property_exists($request->body, 'key')) ? !is_null($request->body->key) : false;
if (!$hasKey && ($request->encapsulation != self::ENCAPSULATION_RAW))
{
$this->password = $this->serverKey();
}
// Does the specified method exist?
$method_exists = false;
$method_name = '';
if (isset($request->body->method))
{
$method_name = ucfirst($request->body->method);
$this->method_name = $method_name;
$method_exists = method_exists($this, '_api' . $method_name);
}
if (!$method_exists)
{
// The requested method doesn't exist. Oops!
$this->data = "Invalid method $method_name";
$this->status = self::STATUS_INVALID_METHOD;
$this->encapsulation = self::ENCAPSULATION_RAW;
return $this->getResponse();
}
// Run the method
$params = array();
if (isset($request->body->data))
{
$params = (array) $request->body->data;
}
$this->data = call_user_func(array($this, '_api' . $method_name), $params);
return $this->getResponse();
}
/**
* Packages the response to a JSON-encoded object, optionally encrypting the
* data part with a caller-supplied password.
*
* @return string The JSON-encoded response
*/
private function getResponse()
{
// Initialize the response
$response = array(
'encapsulation' => $this->encapsulation,
'body' => array(
'status' => $this->status,
'data' => null
)
);
$data = json_encode($this->data);
if (empty($this->password))
{
$this->encapsulation = self::ENCAPSULATION_RAW;
}
switch ($this->encapsulation)
{
case self::ENCAPSULATION_RAW:
break;
case self::ENCAPSULATION_AESCTR128:
$data = Factory::getEncryption()->AESEncryptCtr($data, $this->password, 128);
break;
case self::ENCAPSULATION_AESCTR256:
$data = Factory::getEncryption()->AESEncryptCtr($data, $this->password, 256);
break;
case self::ENCAPSULATION_AESCBC128:
$data = base64_encode(Factory::getEncryption()->AESEncryptCBC($data, $this->password, 128));
break;
case self::ENCAPSULATION_AESCBC256:
$data = base64_encode(Factory::getEncryption()->AESEncryptCBC($data, $this->password, 256));
break;
}
$response['body']['data'] = $data;
return '###' . json_encode($response) . '###';
}
private function serverKey()
{
static $key = null;
if (is_null($key))
{
$key = Platform::getInstance()->get_platform_configuration_option('frontend_secret_word', '');
}
return $key;
}
private function _apiGetVersion()
{
$edition = AKEEBA_PRO ? 'pro' : 'core';
return (object) array(
'api' => AKEEBA_JSON_API_VERSION,
'component' => AKEEBA_VERSION,
'date' => AKEEBA_DATE,
'edition' => $edition,
);
}
private function _apiGetProfiles()
{
require_once JPATH_SITE . '/administrator/components/com_akeeba/models/profiles.php';
$model = new AkeebaModelProfiles();
$profiles = $model->getProfilesList(true);
$ret = array();
if (count($profiles))
{
foreach ($profiles as $profile)
{
$temp = new stdClass();
$temp->id = $profile->id;
$temp->name = $profile->description;
$ret[] = $temp;
}
}
return $ret;
}
private function _apiStartBackup($config)
{
$filter = \JFilterInput::getInstance();
// Get the passed configuration values
$defConfig = array(
'profile' => 1,
'description' => '',
'comment' => '',
'backupid' => null,
'overrides' => array(),
);
$defConfig = array_merge($defConfig, $config);
$profile = (int) $defConfig['profile'];
$profile = max(1, $profile); // Make sure $profile is a positive integer >= 1
$description = $filter->clean($defConfig['description'], 'string');
$comment = $filter->clean($defConfig['comment'], 'string');
$backupid = $filter->clean($defConfig['backupid'], 'cmd');
$backupid = empty($backupid) ? null : $backupid; // Otherwise the Engine doesn't set a backup ID
$overrides = $filter->clean($defConfig['overrides'], 'array');
$session = JFactory::getSession();
$session->set('profile', $profile, 'akeeba');
define('AKEEBA_PROFILE', $profile);
/**
* DO NOT REMOVE!
*
* The Model will only try to load the configuration after nuking the factory. This causes Profile 1 to be
* loaded first. Then it figures out it needs to load a different profile and it does – but the protected keys
* are NOT replaced, meaning that certain configuration parameters are not replaced. Most notably, the chain.
* This causes backups to behave weirdly. So, DON'T REMOVE THIS UNLESS WE REFACTOR THE MODEL.
*/
Platform::getInstance()->load_configuration($profile);
/** @var AkeebaModelBackups $model */
$model = F0FModel::getTmpInstance('Backups', 'AkeebaModel');
$model->setState('tag', AKEEBA_BACKUP_ORIGIN);
$model->setState('backupid', $backupid);
$model->setState('description', $description);
$model->setState('comment', $comment);
$array = $model->startBackup($overrides);
if ($array['Error'] != '')
{
// A backup error had occurred. Why are we here?!
$this->status = self::COM_AKEEBA_CPANEL_LBL_STATUS_ERROR;
$this->encapsulation = self::ENCAPSULATION_RAW;
return 'A backup error has occurred: ' . $array['Error'];
}
$statistics = Factory::getStatistics();
$array['BackupID'] = $statistics->getId();
// Remote clients expect a boolean, not an integer.
$array['HasRun'] = ($array['HasRun'] === 0);
return $array;
}
private function _apiStepBackup($config)
{
$filter = \JFilterInput::getInstance();
$defConfig = array(
'profile' => null,
'tag' => AKEEBA_BACKUP_ORIGIN,
'backupid' => null,
);
$defConfig = array_merge($defConfig, $config);
$profile = $filter->clean($defConfig['profile'], 'int');
$tag = $filter->clean($defConfig['tag'], 'cmd');
$backupid = $filter->clean($defConfig['backupid'], 'cmd');
$session = JFactory::getSession();
// Try to set the profile from the setup parameters
if (!empty($profile))
{
$session->set('profile', $profile);
define('AKEEBA_PROFILE', $profile);
}
/** @var AkeebaModelBackups $model */
$model = F0FModel::getTmpInstance('Backups', 'AkeebaModel');
$model->setState('tag', $tag);
$model->setState('backupid', $backupid);
$array = $model->stepBackup(false);
if ($array['Error'] != '')
{
// A backup error had occurred. Why are we here?!
$this->status = self::COM_AKEEBA_CPANEL_LBL_STATUS_ERROR;
$this->encapsulation = self::ENCAPSULATION_RAW;
return 'A backup error has occurred: ' . $array['Error'];
}
$statistics = Factory::getStatistics();
$array['BackupID'] = $statistics->getId();
// Remote clients expect a boolean, not an integer.
$array['HasRun'] = ($array['HasRun'] === 0);
return $array;
}
private function _apiListBackups($config)
{
$defConfig = array(
'from' => 0,
'limit' => 50
);
$config = array_merge($defConfig, $config);
$from = $config['from'];
$limit = $config['limit'];
require_once JPATH_COMPONENT_ADMINISTRATOR . '/models/statistics.php';
$model = new AkeebaModelStatistics();
$model->setState('limitstart', $from);
$model->setState('limit', $limit);
return $model->getStatisticsListWithMeta(false);
}
private function _apiGetBackupInfo($config)
{
$defConfig = array(
'backup_id' => '0'
);
$config = array_merge($defConfig, $config);
$backup_id = $config['backup_id'];
// Get the basic statistics
$record = Platform::getInstance()->get_statistics($backup_id);
// Get a list of filenames
$backup_stats = Platform::getInstance()->get_statistics($backup_id);
// Backup record doesn't exist
if (empty($backup_stats))
{
$this->status = self::STATUS_NOT_FOUND;
$this->encapsulation = self::ENCAPSULATION_RAW;
return 'Invalid backup record identifier';
}
$filenames = Factory::getStatistics()->get_all_filenames($record);
if (empty($filenames))
{
// Archives are not stored on the server or no files produced
$record['filenames'] = array();
}
else
{
$filedata = array();
$i = 0;
// Get file sizes per part
foreach ($filenames as $file)
{
$i ++;
$size = @filesize($file);
$size = is_numeric($size) ? $size : 0;
$filedata[] = array(
'part' => $i,
'name' => basename($file),
'size' => $size
);
}
// Add the file info to $record['filenames']
$record['filenames'] = $filedata;
}
return $record;
}
private function _apiDownload($config)
{
$defConfig = array(
'backup_id' => 0,
'part_id' => 1,
'segment' => 1,
'chunk_size' => 1
);
$config = array_merge($defConfig, $config);
$backup_id = $config['backup_id'];
$part_id = $config['part_id'];
$segment = $config['segment'];
$chunk_size = $config['chunk_size'];
$backup_stats = Platform::getInstance()->get_statistics($backup_id);
if (empty($backup_stats))
{
// Backup record doesn't exist
$this->status = self::STATUS_NOT_FOUND;
$this->encapsulation = self::ENCAPSULATION_RAW;
return 'Invalid backup record identifier';
}
$files = Factory::getStatistics()->get_all_filenames($backup_stats);
if ((count($files) < $part_id) || ($part_id <= 0))
{
// Invalid part
$this->status = self::STATUS_NOT_FOUND;
$this->encapsulation = self::ENCAPSULATION_RAW;
return 'Invalid backup part';
}
$file = $files[ $part_id - 1 ];
$filesize = @filesize($file);
$seekPos = $chunk_size * 1048756 * ($segment - 1);
if ($seekPos > $filesize)
{
// Trying to seek past end of file
$this->status = self::STATUS_NOT_FOUND;
$this->encapsulation = self::ENCAPSULATION_RAW;
return 'Invalid segment';
}
$fp = fopen($file, 'rb');
if ($fp === false)
{
// Could not read file
$this->status = self::COM_AKEEBA_CPANEL_LBL_STATUS_ERROR;
$this->encapsulation = self::ENCAPSULATION_RAW;
return 'Error reading backup archive';
}
rewind($fp);
if (fseek($fp, $seekPos, SEEK_SET) === - 1)
{
// Could not seek to position
$this->status = self::COM_AKEEBA_CPANEL_LBL_STATUS_ERROR;
$this->encapsulation = self::ENCAPSULATION_RAW;
return 'Error reading specified segment';
}
$buffer = fread($fp, 1048756);
if ($buffer === false)
{
// Could not read
$this->status = self::COM_AKEEBA_CPANEL_LBL_STATUS_ERROR;
$this->encapsulation = self::ENCAPSULATION_RAW;
return 'Error reading specified segment';
}
fclose($fp);
switch ($this->encapsulation)
{
case self::ENCAPSULATION_RAW:
return base64_encode($buffer);
break;
case self::ENCAPSULATION_AESCTR128:
$this->encapsulation = self::ENCAPSULATION_AESCBC128;
return $buffer;
break;
case self::ENCAPSULATION_AESCTR256:
$this->encapsulation = self::ENCAPSULATION_AESCBC256;
return $buffer;
break;
default:
// On encrypted comms the encryption will take care of transport encoding
return $buffer;
break;
}
}
private function _apiDelete($config)
{
$defConfig = array(
'backup_id' => 0
);
$config = array_merge($defConfig, $config);
$backup_id = $config['backup_id'];
require_once JPATH_COMPONENT_ADMINISTRATOR . '/models/statistics.php';
$model = new AkeebaModelStatistics();
$model->setState('id', (int) $backup_id);
$result = $model->delete();
if (!$result)
{
$this->status = self::COM_AKEEBA_CPANEL_LBL_STATUS_ERROR;
$this->encapsulation = self::ENCAPSULATION_RAW;
return $model->getError();
}
else
{
return true;
}
}
private function _apiDeleteFiles($config)
{
$defConfig = array(
'backup_id' => 0
);
$config = array_merge($defConfig, $config);
$backup_id = $config['backup_id'];
require_once JPATH_COMPONENT_ADMINISTRATOR . '/models/statistics.php';
$model = new AkeebaModelStatistics();
$model->setState('id', (int) $backup_id);
$result = $model->deleteFile();
if (!$result)
{
$this->status = self::COM_AKEEBA_CPANEL_LBL_STATUS_ERROR;
$this->encapsulation = self::ENCAPSULATION_RAW;
return $model->getError();
}
else
{
return true;
}
}
private function _apiGetLog($config)
{
$defConfig = array(
'tag' => 'remote'
);
$config = array_merge($defConfig, $config);
$tag = $config['tag'];
$filename = Factory::getLog()->getLogFilename($tag);
$buffer = file_get_contents($filename);
switch ($this->encapsulation)
{
case self::ENCAPSULATION_RAW:
return base64_encode($buffer);
break;
case self::ENCAPSULATION_AESCTR128:
$this->encapsulation = self::ENCAPSULATION_AESCBC128;
return $buffer;
break;
case self::ENCAPSULATION_AESCTR256:
$this->encapsulation = self::ENCAPSULATION_AESCBC256;
return $buffer;
break;
default:
// On encrypted comms the encryption will take care of transport encoding
return $buffer;
break;
}
}
private function _apiDownloadDirect($config)
{
$defConfig = array(
'backup_id' => 0,
'part_id' => 1
);
$config = array_merge($defConfig, $config);
$backup_id = $config['backup_id'];
$part_id = $config['part_id'];
$backup_stats = Platform::getInstance()->get_statistics($backup_id);
if (empty($backup_stats))
{
// Backup record doesn't exist
$this->status = self::STATUS_NOT_FOUND;
$this->encapsulation = self::ENCAPSULATION_RAW;
@ob_end_clean();
header('HTTP/1.1 500 Invalid backup record identifier');
flush();
JFactory::getApplication()->close();
}
$files = Factory::getStatistics()->get_all_filenames($backup_stats);
if ((count($files) < $part_id) || ($part_id <= 0))
{
// Invalid part
$this->status = self::STATUS_NOT_FOUND;
$this->encapsulation = self::ENCAPSULATION_RAW;
@ob_end_clean();
header('HTTP/1.1 500 Invalid backup part');
flush();
JFactory::getApplication()->close();
}
$filename = $files[ $part_id - 1 ];
@clearstatcache();
// For a certain unmentionable browser -- Thank you, Nooku, for the tip
if (function_exists('ini_get') && function_exists('ini_set'))
{
if (ini_get('zlib.output_compression'))
{
ini_set('zlib.output_compression', 'Off');
}
}
// Remove php's time limit -- Thank you, Nooku, for the tip
if (function_exists('ini_get') && function_exists('set_time_limit'))
{
if (!ini_get('safe_mode'))
{
@set_time_limit(0);
}
}
$basename = @basename($filename);
$filesize = @filesize($filename);
$extension = strtolower(str_replace(".", "", strrchr($filename, ".")));
while (@ob_end_clean())
{
;
}
@clearstatcache();
// Send MIME headers
header('MIME-Version: 1.0');
header('Content-Disposition: attachment; filename="' . $basename . '"');
header('Content-Transfer-Encoding: binary');
header('Accept-Ranges: bytes');
switch ($extension)
{
case 'zip':
// ZIP MIME type
header('Content-Type: application/zip');
break;
default:
// Generic binary data MIME type
header('Content-Type: application/octet-stream');
break;
}
// Notify of filesize, if this info is available
if ($filesize > 0)
{
header('Content-Length: ' . @filesize($filename));
}
// Disable caching
header("Cache-Control: must-revalidate, post-check=0, pre-check=0");
header("Expires: 0");
header('Pragma: no-cache');
flush();
if ($filesize > 0)
{
// If the filesize is reported, use 1M chunks for echoing the data to the browser
$blocksize = 1048756; //1M chunks
$handle = @fopen($filename, "r");
// Now we need to loop through the file and echo out chunks of file data
if ($handle !== false)
{
while (!@feof($handle))
{
echo @fread($handle, $blocksize);
@ob_flush();
flush();
}
}
if ($handle !== false)
{
@fclose($handle);
}
}
else
{
// If the filesize is not reported, hope that readfile works
@readfile($filename);
}
flush();
JFactory::getApplication()->close();
}
}