| Current Path : /proc/thread-self/root/tmp/ |
| Current File : //proc/thread-self/root/tmp/phpjsSzwu |
<?php
/**
* @package akeebabackup
* @copyright Copyright (c)2006-2023 Nicholas K. Dionysopoulos / Akeeba Ltd
* @license GNU General Public License version 3, or later
*/
namespace Akeeba\Backup\Site\Controller\Mixin;
// Protect from unauthorized access
use Akeeba\Engine\Platform;
defined('_JEXEC') || die();
/**
* Provides the method to set the current backup profile from the request variables
*/
trait ActivateProfile
{
/**
* Set the active profile from the input parameters
*/
protected function setProfile()
{
$profile = $this->input->get('profile', 1, 'int');
$profile = max(1, $profile);
$this->container->platform->setSessionVar('profile', $profile, 'akeeba');
/**
* 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);
}
}
<?php
/**
* @package akeebabackup
* @copyright Copyright (c)2006-2023 Nicholas K. Dionysopoulos / Akeeba Ltd
* @license GNU General Public License version 3, or later
*/
namespace Akeeba\Backup\Site\Controller\Mixin;
// Protect from unauthorized access
use Akeeba\Engine\Platform;
defined('_JEXEC') || die();
/**
* Provides the method to send custom HTTP redirection headers
*/
trait CustomRedirection
{
/**
* Sends custom HTTP redirection headers
*
* @param string $url The URL to redirect to
* @param string $header The HTTP header to send, default 302 Found
*/
protected function customRedirect($url, $header = '302 Found')
{
header('HTTP/1.1 ' . $header);
header('Location: ' . $url);
header('Content-Type: text/plain');
header('Connection: close');
$this->container->platform->closeApplication();
}
}
<?php
/**
* @package akeebabackup
* @copyright Copyright (c)2006-2023 Nicholas K. Dionysopoulos / Akeeba Ltd
* @license GNU General Public License version 3, or later
*/
namespace Akeeba\Backup\Site\Controller\Mixin;
// Protect from unauthorized access
use Akeeba\Engine\Platform;
use Akeeba\Engine\Util\Complexify;
use DateInterval;
use Exception;
use FOF40\Date\Date;
use Joomla\CMS\Language\Text;
defined('_JEXEC') || die();
/**
* Provides the method to check whether front-end backup is enabled and weather the key is correct
*/
trait FrontEndPermissions
{
private static $ENABLE_DATE_CHECKS = false;
/**
* Check that the user has sufficient permissions to access the front-end backup feature.
*
* @return void
*/
protected function checkPermissions()
{
// Is frontend backup enabled?
$febEnabled = $this->container->params->get('legacyapi_enabled', 0) == 1;
// Is the Secret Key strong enough?
$validKey = Platform::getInstance()->get_platform_configuration_option('frontend_secret_word', '');
$validKeyTrim = trim($validKey);
if (!Complexify::isStrongEnough($validKey, false))
{
$febEnabled = false;
}
if (static::$ENABLE_DATE_CHECKS && !$this->confirmDates())
{
@ob_end_clean();
echo '402 Your version of Akeeba Backup is too old. Please update it to re-enable the remote backup features';
flush();
$this->container->platform->closeApplication();
}
// Is the key good?
$key = $this->input->get('key', '', 'none', 2);
if (!$febEnabled || ($key != $validKey) || (empty($validKeyTrim)))
{
@ob_end_clean();
echo sprintf("403 %s", Text::_('COM_AKEEBA_COMMON_ERR_NOT_ENABLED'));
flush();
$this->container->platform->closeApplication();
}
}
private function confirmDates()
{
if (!defined('AKEEBA_DATE'))
{
return false;
}
try
{
$jDate = new Date(AKEEBA_DATE);
$interval = new DateInterval('P4M');
$jFuture = $jDate->add($interval);
$futureTS = $jFuture->toUnix();
}
catch (Exception $e)
{
return false;
}
return time() <= $futureTS;
}
}
<?php
/**
* @package akeebabackup
* @copyright Copyright (c)2006-2023 Nicholas K. Dionysopoulos / Akeeba Ltd
* @license GNU General Public License version 3, or later
*/
namespace Akeeba\Backup\Site\Controller;
// Protect from unauthorized access
defined('_JEXEC') || die();
use Akeeba\Backup\Site\Model\Json\Task;
use Akeeba\Engine\Platform;
use Akeeba\Engine\Util\Complexify;
use Exception;
use FOF40\Container\Container;
use FOF40\Controller\Controller;
use FOF40\Controller\Mixin\PredefinedTaskList;
use FOF40\Input\Input;
use Joomla\CMS\Document\Document;
use Joomla\CMS\Document\JsonDocument;
use Joomla\CMS\Factory;
use JsonSerializable;
/**
* API version
*
* 400: First JSON API v2 implementation
*/
if (!defined('AKEEBA_JSON_API_VERSION'))
{
define('AKEEBA_JSON_API_VERSION', 400);
}
/**
* Akeeba Backup JSON API v2
*
* @since 7.4.0
*/
class Api extends Controller
{
use PredefinedTaskList;
/**
* Secret Key (cached for quicker retrieval)
*
* @var null|string
* @since 7.4.0
*/
private $key = null;
/**
* Overridden constructor
*
* @param Container $container The application container
* @param array $config The configuration array
*
* @since 7.4.0
*/
public function __construct(Container $container, array $config)
{
parent::__construct($container, $config);
$this->setPredefinedTaskList(['main']);
}
public function main()
{
if (!defined('AKEEBA_BACKUP_ORIGIN'))
{
define('AKEEBA_BACKUP_ORIGIN', 'json');
}
$outputBuffering = function_exists('ob_start') && function_exists('ob_end_clean');
// Use the model to parse the JSON message
if ($outputBuffering)
{
@ob_start();
}
try
{
if (!$this->verifyKey())
{
throw new \RuntimeException("Access denied", 503);
}
$httpVerb = $this->input->getMethod() ?? 'GET';
switch ($httpVerb)
{
case 'GET':
$method = $this->input->get->getCmd('method', '');
$input = new Input('GET');
break;
case 'POST':
$method = $this->input->post->getCmd('method', '');
$input = new Input('POST');
break;
default:
throw new \RuntimeException("Invalid HTTP method {$httpVerb}", 405);
break;
}
if (class_exists('Joomla\CMS\Component\ComponentHelper') && \Joomla\CMS\Component\ComponentHelper::isEnabled('com_akeebabackup'))
{
throw new \RuntimeException(sprintf('Please finish upgrading to Akeeba Backup 9 and uninstall Akeeba Backup 8 per the instructions shown on your site\'s backend, Components, Akeeba Backup'), 400);
}
$taskHandler = new Task($this->container);
$result = [
'status' => 200,
'data' => $taskHandler->execute($method, $input->getData())
];
}
catch (Exception $e)
{
$result = [
'status' => $e->getCode(),
'data' => $e->getMessage(),
];
}
if ($outputBuffering)
{
@ob_end_clean();
}
/** @var JsonDocument $doc */
$doc = Document::getInstance('json');
if (!($doc instanceof JsonDocument))
{
$this->workaroundResponse($result);
}
// Force cache busting
$app = $this->container->platform;
$app->setHeader('Expires', 'Wed, 17 Aug 2005 00:00:00 GMT', true);
$app->setHeader('Cache-Control', 'no-store, no-cache, must-revalidate, post-check=0, pre-check=0', true);
$app->setHeader('Pragma', 'no-cache', true);
$doc->setName('akeeba');
$jsonOptions = (defined('JDEBUG') && JDEBUG) ? JSON_PRETTY_PRINT : 0;
echo json_encode($result, $jsonOptions);
}
/**
* Send a JSON response when format=html or anything other than json
*
* @param JsonSerializable|array $result
*
* @throws Exception
*
* @since 7.4.0
*/
private function workaroundResponse($result): void
{
// Disable caching
@header('Expires: Wed, 17 Aug 2005 00:00:00 GMT', true);
@header('Cache-Control: no-store, no-cache, must-revalidate, post-check=0, pre-check=0', true);
@header('Pragma: no-cache', true);
// JSON content
@header('Content-Type: application/json; charset=utf-8', true);
@header('Content-Disposition: attachment; filename="joomla.json"', true);
$jsonOptions = (defined('JDEBUG') && JDEBUG) ? JSON_PRETTY_PRINT : 0;
echo json_encode($result, $jsonOptions);
Factory::getApplication()->close();
}
/**
* Verifies the Secret Key (API token)
*
* @return bool
* @since 7.4.0
*/
private function verifyKey(): bool
{
// Is the JSON API enabled?
if ($this->container->params->get('jsonapi_enabled', 0) != 1)
{
return false;
}
// Is the key secure enough?
$validKey = $this->serverKey();
if (empty($validKey) || empty(trim($validKey)) || !Complexify::isStrongEnough($validKey, false))
{
return false;
}
/**
* Get the API authentication token. There are two sources
* 1. X-Akeeba-Auth header (preferred, overrides all others)
* 2. the _akeebaAuth GET parameter
*/
$authSource = $this->input->server->getString('HTTP_X_AKEEBA_AUTH', null);
if (is_null($authSource))
{
$authSource = $this->input->get->getString('_akeebaAuth', null);
}
// No authentication token? No joy.
if (empty($authSource) || !is_string($authSource) || empty(trim($authSource)))
{
return false;
}
return hash_equals($validKey, $authSource);
}
/**
* Get the server key, i.e. the Secret Word for the front-end backups and JSON API
*
* @return mixed
*
* @since 7.4.0
*/
private function serverKey()
{
if (is_null($this->key))
{
$this->key = Platform::getInstance()->get_platform_configuration_option('frontend_secret_word', '');
}
return $this->key;
}
}
<?php
/**
* @package akeebabackup
* @copyright Copyright (c)2006-2023 Nicholas K. Dionysopoulos / Akeeba Ltd
* @license GNU General Public License version 3, or later
*/
namespace Akeeba\Backup\Site\Controller;
// Protect from unauthorized access
defined('_JEXEC') || die();
use Akeeba\Backup\Site\Controller\Mixin\ActivateProfile;
use Akeeba\Backup\Site\Controller\Mixin\CustomRedirection;
use Akeeba\Backup\Site\Controller\Mixin\FrontEndPermissions;
use Akeeba\Engine\Factory;
use FOF40\Container\Container;
use FOF40\Controller\Controller;
use FOF40\Controller\Mixin\PredefinedTaskList;
use FOF40\Date\Date;
use Joomla\CMS\Language\Text;
use Joomla\CMS\Router\Route;
use Joomla\CMS\Uri\Uri;
if (!defined('AKEEBA_BACKUP_ORIGIN'))
{
define('AKEEBA_BACKUP_ORIGIN', 'frontend');
}
/**
* Controller for the front-end backup feature.
*
* The Traits used by this class offer most of the features you don't see, especially those pertaining to security:
* PredefinedTaskList Only allows certain tasks to be called.
* FrontEndPermissions Validates the secret word before running a task through checkPermissions.
* ActivateProfile Finds the profile specified in the URL and loads it through setProfile.
* CustomRedirection Provides customRedirect for HTTP redirects without dealing with CMS inconsistencies.
*/
class Backup extends Controller
{
use PredefinedTaskList, FrontEndPermissions, ActivateProfile, CustomRedirection;
/**
* Overridden constructor
*
* @param Container $container The application container
* @param array $config The configuration array
*/
public function __construct(Container $container, array $config)
{
parent::__construct($container, $config);
$this->setPredefinedTaskList(['main', 'step']);
}
/**
* Start a front-end legacy backup
*
* @return void
*/
public function main()
{
$this->checkPermissions();
$this->setProfile();
if (class_exists('Joomla\CMS\Component\ComponentHelper') && \Joomla\CMS\Component\ComponentHelper::isEnabled('com_akeebabackup'))
{
@ob_end_clean();
echo '500 ERROR -- Please finish upgrading to Akeeba Backup 9 and uninstall Akeeba Backup 8 per the instructions shown on your site\'s backend, Components, Akeeba Backup';
flush();
$this->container->platform->closeApplication();
}
// Get the backup ID
$backupId = $this->input->get('backupid', null, 'cmd');
if (empty($backupId))
{
$backupId = null;
}
/** @var \Akeeba\Backup\Site\Model\Backup $model */
$model = $this->container->factory->model('Backup')->tmpInstance();
$dateNow = new Date();
$model->setState('tag', AKEEBA_BACKUP_ORIGIN);
$model->setState('backupid', $backupId);
$model->setState('description', $model->getDefaultDescription() . ' (Frontend)');
$model->setState('comment', '');
$array = $model->startBackup();
$backupId = $model->getState('backupid', null, 'cmd');
$this->processEngineReturnArray($array, $backupId);
}
/**
* Step through a front-end legacy backup
*
* @return void
*/
public function step()
{
// Setup
$this->checkPermissions();
$this->setProfile();
// Get the backup ID
$backupId = $this->input->get('backupid', null, 'cmd');
if (empty($backupId))
{
$backupId = null;
}
/** @var \Akeeba\Backup\Site\Model\Backup $model */
$model = $this->container->factory->model('Backup')->tmpInstance();
$model->setState('tag', AKEEBA_BACKUP_ORIGIN);
$model->setState('backupid', $backupId);
$array = $model->stepBackup();
$backupId = $model->getState('backupid', null, 'cmd');
$this->processEngineReturnArray($array, $backupId);
}
/**
* Used by the tasks to process Akeeba Engine's return array. Depending on the result and the component options we
* may throw text output or send an HTTP redirection header.
*
* @param array $array The return array to process
* @param string $backupId The backup ID (used to step the backup process)
*/
private function processEngineReturnArray($array, $backupId)
{
if ($array['Error'] != '')
{
@ob_end_clean();
echo '500 ERROR -- ' . $array['Error'];
flush();
$this->container->platform->closeApplication();
}
if ($array['HasRun'] == 1)
{
// All done
Factory::nuke();
Factory::getFactoryStorage()->reset();
@ob_end_clean();
header('Content-type: text/plain');
header('Connection: close');
echo '200 OK';
flush();
$this->container->platform->closeApplication();
}
$noredirect = $this->input->get('noredirect', 0, 'int');
if ($noredirect != 0)
{
@ob_end_clean();
header('Content-type: text/plain');
header('Connection: close');
echo "301 More work required -- BACKUPID ###$backupId###";
flush();
$this->container->platform->closeApplication();
}
$curUri = Uri::getInstance();
$ssl = $curUri->isSSL() ? 1 : 0;
$tempURL = Route::_('index.php?option=com_akeeba', false, $ssl);
$uri = new Uri($tempURL);
$uri->delVar('key');
$uri->setVar('view', 'Backup');
$uri->setVar('task', 'step');
$uri->setVar('profile', $this->input->get('profile', 1, 'int'));
if (!empty($backupId))
{
$uri->setVar('backupid', $backupId);
}
// Maybe we have a multilingual site?
$language = $this->container->platform->getLanguage();
$languageTag = $language->getTag();
$uri->setVar('lang', $languageTag);
$key = $this->input->get('key', '', 'none', 2);
$redirectionUrl = $uri->toString() . '&key=' . urlencode($key);
$this->customRedirect($redirectionUrl);
}
}
<?php
/**
* @package akeebabackup
* @copyright Copyright (c)2006-2023 Nicholas K. Dionysopoulos / Akeeba Ltd
* @license GNU General Public License version 3, or later
*/
namespace Akeeba\Backup\Site\Controller;
// Protect from unauthorized access
defined('_JEXEC') || die();
use Akeeba\Backup\Site\Controller\Mixin\FrontEndPermissions;
use Akeeba\Backup\Site\Model\Statistics;
use FOF40\Container\Container;
use FOF40\Controller\Controller;
use FOF40\Controller\Mixin\PredefinedTaskList;
/**
* Controller for the front-end Check Backups features
*/
class Check extends Controller
{
use PredefinedTaskList, FrontEndPermissions;
/**
* Overridden constructor
*
* @param Container $container The application container
* @param array $config The configuration array
*/
public function __construct(Container $container, array $config)
{
parent::__construct($container, $config);
$this->setPredefinedTaskList(['main']);
}
/**
* Checks for failed backups and sends out any notification emails
*/
public function main()
{
// Check permissions
$this->checkPermissions();
/** @var Statistics $model */
$model = $this->container->factory->model('Statistics')->tmpInstance();
$result = $model->notifyFailed();
$message = $result['result'] ? '200 ' : '500 ';
$message .= implode(', ', $result['message']);
@ob_end_clean();
header('Content-type: text/plain');
header('Connection: close');
echo $message;
flush();
$this->container->platform->closeApplication();
}
}
<?php
/**
* @package akeebabackup
* @copyright Copyright (c)2006-2023 Nicholas K. Dionysopoulos / Akeeba Ltd
* @license GNU General Public License version 3, or later
*/
namespace Akeeba\Backup\Site\Controller;
// Protect from unauthorized access
defined('_JEXEC') || die();
use FOF40\Container\Container;
use FOF40\Controller\Controller;
use FOF40\Controller\Mixin\PredefinedTaskList;
/**
* Controller for the JSON API
*/
class Json extends Controller
{
use PredefinedTaskList;
/**
* Overridden constructor
*
* @param Container $container The application container
* @param array $config The configuration array
*/
public function __construct(Container $container, array $config)
{
parent::__construct($container, $config);
$this->setPredefinedTaskList(['json']);
}
/**
* Handles API calls
*/
public function json()
{
// Use the model to parse the JSON message
if (function_exists('ob_start'))
{
@ob_start();
}
$sourceJSON = $this->input->get('json', null, 'raw', 2);
/** @var \Akeeba\Backup\Site\Model\Json $model */
$model = $this->getModel();
$json = $model->execute($sourceJSON);
if (function_exists('ob_end_clean'))
{
@ob_end_clean();
}
// Just dump the JSON and tear down the application, without plugins executing
header('Content-type: text/plain');
header('Connection: close');
echo $json;
$this->container->platform->closeApplication();
}
}
<!--~
~ @package akeebabackup
~ @copyright Copyright (c)2006-2023 Nicholas K. Dionysopoulos / Akeeba Ltd
~ @license GNU General Public License version 3, or later
-->
<html><head><title></title></head><body></body></html><?xml version="1.0"?>
<!--
This only works on IIS 7 or later. See https://www.iis.net/configreference/system.webserver/security/requestfiltering/fileextensions
-->
<configuration>
<system.webServer>
<security>
<requestFiltering>
<fileExtensions allowUnlisted="false" >
<clear />
<add fileExtension=".html" allowed="true"/>
</fileExtensions>
</requestFiltering>
</security>
</system.webServer>
</configuration><?php
/**
* @package akeebabackup
* @copyright Copyright (c)2006-2023 Nicholas K. Dionysopoulos / Akeeba Ltd
* @license GNU General Public License version 3, or later
*/
namespace Akeeba\Backup\Site\Dispatcher;
// Protect from unauthorized access
defined('_JEXEC') || die();
use Akeeba\Backup\Admin\Dispatcher\Dispatcher as AdminDispatcher;
use Akeeba\Backup\Admin\Helper\SecretWord;
use Akeeba\Engine\Factory;
use Akeeba\Engine\Platform;
use FOF40\Container\Container;
use FOF40\Dispatcher\Exception\AccessForbidden;
use Joomla\CMS\Document\Document;
use Joomla\CMS\Document\JsonDocument as JDocumentJSON;
use Joomla\CMS\Factory as JFactory;
use Joomla\CMS\Language\Text;
class Dispatcher extends AdminDispatcher
{
/** @var string The name of the default view, in case none is specified */
public $defaultView = 'Backup';
/**
* Dispatcher constructor. Overridden to set up a different default view and migrated views map than the back-end.
*
* @param Container $container The component's container
* @param array $config Optional configuration overrides
*/
public function __construct(Container $container, array $config)
{
parent::__construct($container, $config);
$this->defaultView = 'Backup';
$this->viewNameAliases = [
'backup' => 'Backup',
'backups' => 'Backup',
'check' => 'Check',
'checks' => 'Check',
'json' => 'Json',
'jsons' => 'Json',
];
}
/**
* Executes before dispatching the request to the appropriate controller
*/
public function onBeforeDispatch()
{
// Make sure we have a version loaded
@include_once($this->container->backEndPath . '/version.php');
if (!defined('AKEEBA_VERSION'))
{
define('AKEEBA_VERSION', 'dev');
define('AKEEBA_DATE', date('Y-m-d'));
}
// Core version: there is no front-end, throw a 403
if (!defined('AKEEBA_PRO') || !AKEEBA_PRO)
{
throw new AccessForbidden(Text::_('COM_AKEEBA_ERR_NO_FRONTEND_IN_CORE'));
}
// $this->container->platform->importPlugin('akeebabackup');
// $this->container->platform->runPlugins('onComAkeebaDispatcherBeforeDispatch', []);
$this->onBeforeDispatchViewAliases();
// Load the FOF language
$lang = $this->container->platform->getLanguage();
$lang->load('lib_fof40', JPATH_SITE, 'en-GB', true, true);
$lang->load('lib_fof40', JPATH_SITE, null, true, false);
// Necessary defines for Akeeba Engine
if (!defined('AKEEBAENGINE'))
{
define('AKEEBAENGINE', 1);
define('AKEEBAROOT', $this->container->backEndPath . '/BackupEngine');
define('ALICEROOT', $this->container->backEndPath . '/AliceEngine');
}
// Make sure we have a profile set throughout the component's lifetime
$profile_id = $this->container->platform->getSessionVar('profile', null, 'akeeba');
if (is_null($profile_id))
{
$this->container->platform->setSessionVar('profile', 1, 'akeeba');
}
// Load Akeeba Engine
$basePath = $this->container->backEndPath;
require_once $basePath . '/BackupEngine/Factory.php';
// Load the Akeeba Engine configuration
Platform::addPlatform('joomla3x', JPATH_COMPONENT_ADMINISTRATOR . '/BackupPlatform/Joomla3x');
$akeebaEngineConfig = Factory::getConfiguration();
Platform::getInstance()->load_configuration();
unset($akeebaEngineConfig);
// Prevents the "SQLSTATE[HY000]: General error: 2014" due to resource sharing with Akeeba Engine
$this->fixPDOMySQLResourceSharing();
// Load the utils helper library
Platform::getInstance()->load_version_defines();
// Make sure the front-end backup Secret Word is stored encrypted
$params = $this->container->params;
SecretWord::enforceEncryption($params, 'frontend_secret_word');
// Create a media file versioning tag
$this->container->mediaVersion = md5(AKEEBA_VERSION . AKEEBA_DATE);
}
public function onAfterDispatch()
{
// Make sure that Api and Json views forcibly get format=json
if (in_array($this->view, ['Api', 'Json']))
{
$format = $this->input->getCmd('format', 'html');
if ($format == 'json')
{
return;
}
$app = JFactory::getApplication();
// Disable caching, disable offline, force use of index.php
$app->set('caching', 0);
$app->set('offline', 0);
$app->set('themeFile', 'index.php');
/** @var \Joomla\CMS\Document\JsonDocument $doc */
$doc = Document::getInstance('json');
$app->loadDocument($doc);
if (property_exists(JFactory::class, 'document'))
{
JFactory::$document = $doc;
}
// Set a custom document name
/** @var JDocumentJSON $document */
$document = $this->container->platform->getDocument();
$document->setName('akeeba_backup');
}
}
}
<?xml version="1.0"?>
<!--
This only works on IIS 7 or later. See https://www.iis.net/configreference/system.webserver/security/requestfiltering/fileextensions
-->
<configuration>
<system.webServer>
<security>
<requestFiltering>
<fileExtensions allowUnlisted="false" >
<clear />
<add fileExtension=".html" allowed="true"/>
</fileExtensions>
</requestFiltering>
</security>
</system.webServer>
</configuration><?php
/**
* @package akeebabackup
* @copyright Copyright (c)2006-2023 Nicholas K. Dionysopoulos / Akeeba Ltd
* @license GNU General Public License version 3, or later
*/
namespace Akeeba\Backup\Site\Model\Json\Encapsulation;
// Protect from unauthorized access
defined('_JEXEC') || die();
use Akeeba\Backup\Site\Model\Json\EncapsulationInterface;
use Akeeba\Engine\Factory;
use Akeeba\Engine\Util\Encrypt;
abstract class Base implements EncapsulationInterface
{
/**
* The numeric ID of this encapsulation
*
* @var int
*/
protected $id = 0;
/**
* The code of this encapsulation
*
* @var string
*/
protected $code = 'ENCAPSULATION_VOID';
/**
* The description of this encapsulation
*
* @var string
*/
protected $description = 'Invalid encapsulation';
/**
* The encryption object which is set up for use with the JSON API
*
* @var Encrypt
*/
private $encryption;
/**
* Public constructor. Called by children to customise the encapsulation handler object
*
* @param int $id Numeric ID
* @param string $code Code
* @param string $description Human readable description
*/
function __construct($id, $code, $description)
{
$this->id = $id;
$this->code = strtoupper($code);
$this->description = $description;
}
/**
* Returns information about the encapsulation supported by this class. The return array has the following keys:
* id: The numeric ID of the encapsulation, e.g. 3
* code: The short code of the encapsulation, e.g. ENCAPSULATION_AESCTR256
* description: A human readable descriptions, e.g. "Data in AES-256 stream (CTR) mode encrypted JSON"
*
* @return array See above
*/
public function getInformation()
{
return array(
'id' => $this->id,
'code' => $this->code,
'description' => $this->description,
);
}
/**
* Checks if the request body authorises the user to use the API. Each encapsulation can implement its own
* authorisation method. This method is only called after the request body has been successfully decoded, therefore
* encrypted encapsulations can simply return true.
*
* @param string $serverKey The server key we need to check the authorisation
* @param array $body The decoded body (as returned by the decode() method)
*
* @return bool True if authorised
*/
public function isAuthorised($serverKey, $body)
{
return true;
}
/**
* Is the provided encapsulation type supported by this class?
*
* @param int $encapsulation Encapsulation type
*
* @return bool True if supported
*/
public function isSupported($encapsulation)
{
return $encapsulation == $this->id;
}
/**
* Decodes the data. For encrypted encapsulations this means base64-decoding the data, decrypting it and then JSON-
* decoding the result. If any error occurs along the way the appropriate exception is thrown.
*
* The data being decoded corresponds to the Request Body described in the API documentation
*
* @param string $serverKey The server key we need to decode data
* @param string $data Encoded data
*
* @return string The decoded data.
*
* @throws \RuntimeException When the server capabilities don't match the requested encapsulation
* @throws \InvalidArgumentException When $data cannot be decoded successfully
*
* @see https://www.akeeba.com/documentation/json-api/ar01s02.html
*/
public function decode($serverKey, $data)
{
return $data;
}
/**
* Encodes the data. The data is JSON encoded by this method before encapsulation takes place. Encrypted
* encapsulations will then encrypt the data and base64-encode it before returning it.
*
* The data being encoded correspond to the body > data structure described in the API documentation
*
* @param string $serverKey The server key we need to encode data
* @param mixed $data The data to encode, typically a string, array or object
*
* @return string The encapsulated data
*
* @see https://www.akeeba.com/documentation/json-api/ar01s02s02.html
*
* @throws \RuntimeException When the server capabilities don't match the requested encapsulation
* @throws \InvalidArgumentException When $data cannot be converted to JSON
*/
public function encode($serverKey, $data)
{
return $data;
}
}
<?php
/**
* @package akeebabackup
* @copyright Copyright (c)2006-2023 Nicholas K. Dionysopoulos / Akeeba Ltd
* @license GNU General Public License version 3, or later
*/
namespace Akeeba\Backup\Site\Model\Json\Encapsulation;
// Protect from unauthorized access
defined('_JEXEC') || die();
/**
* Raw (plain text) encapsulation
*/
class Raw extends Base
{
/**
* Constructs the encapsulation handler object
*/
function __construct()
{
parent::__construct(1, 'ENCAPSULATION_RAW', 'Data in plain-text JSON');
}
/**
* Decodes the data. For encrypted encapsulations this means base64-decoding the data, decrypting it and then JSON-
* decoding the result. If any error occurs along the way the appropriate exception is thrown.
*
* The data being decoded corresponds to the Request Body described in the API documentation
*
* @param string $serverKey The server key we need to decode data
* @param string $data Encoded data
*
* @return string The decoded data.
*
* @throws \RuntimeException When the server capabilities don't match the requested encapsulation
* @throws \InvalidArgumentException When $data cannot be decoded successfully
*
* @see https://www.akeeba.com/documentation/json-api/ar01s02.html
*/
public function decode($serverKey, $data)
{
return $data;
}
/**
* Encodes the data. The data is JSON encoded by this method before encapsulation takes place. Encrypted
* encapsulations will then encrypt the data and base64-encode it before returning it.
*
* The data being encoded correspond to the body > data structure described in the API documentation
*
* @param string $serverKey The server key we need to encode data
* @param mixed $data The data to encode, typically a string, array or object
*
* @return string The encapsulated data
*
* @see https://www.akeeba.com/documentation/json-api/ar01s02s02.html
*
* @throws \RuntimeException When the server capabilities don't match the requested encapsulation
* @throws \InvalidArgumentException When $data cannot be converted to JSON
*/
public function encode($serverKey, $data)
{
return $data;
}
/**
* Checks if the request body authorises the user to use the API. Each encapsulation can implement its own
* authorisation method. This method is only called after the request body has been successfully decoded, therefore
* encrypted encapsulations can simply return true.
*
* @param string $serverKey The server key we need to check the authorisation
* @param array $body The decoded body (as returned by the decode() method)
*
* @return bool True if authorised
*/
public function isAuthorised($serverKey, $body)
{
$authenticated = false;
if (isset($body['challenge']) && (strpos($body['challenge'], ':') >= 2) && (strlen($body['challenge']) >= 3))
{
[$challengeData, $providedHash] = explode(':', $body['challenge']);
$computedHash = strtolower(md5($challengeData . $serverKey));
$authenticated = ($computedHash == $providedHash);
}
return $authenticated;
}
}
<?php
/**
* @package akeebabackup
* @copyright Copyright (c)2006-2023 Nicholas K. Dionysopoulos / Akeeba Ltd
* @license GNU General Public License version 3, or later
*/
namespace Akeeba\Backup\Site\Model\Json\Task;
// Protect from unauthorized access
defined('_JEXEC') || die();
// Protect from unauthorized access
use Akeeba\Backup\Site\Model\Json\TaskInterface;
use FOF40\Container\Container;
class AbstractTask implements TaskInterface
{
/**
* The container of the component we belong to
*
* @var Container
*/
protected $container = null;
/**
* The method name
*
* @var string
*/
protected $methodName = '';
/**
* Public constructor
*
* @param Container $container The container of the component we belong to
*/
public function __construct(Container $container)
{
$this->container = $container;
$path = explode('\\', get_class($this));
$shortName = array_pop($path);
$this->methodName = lcfirst($shortName);
}
/**
* Return the JSON API task's name ("method" name). Remote clients will use it to call us.
*
* @return string
*/
public function getMethodName()
{
return $this->methodName;
}
/**
* Execute the JSON API task
*
* @param array $parameters The parameters to this task
*
* @return mixed
*
* @throws \RuntimeException In case of an error
*/
public function execute(array $parameters = array())
{
throw new \LogicException(__CLASS__ . ' has not implemented its execute() method yet.');
}
}
<?php
/**
* @package akeebabackup
* @copyright Copyright (c)2006-2023 Nicholas K. Dionysopoulos / Akeeba Ltd
* @license GNU General Public License version 3, or later
*/
namespace Akeeba\Backup\Site\Model\Json\Task;
// Protect from unauthorized access
defined('_JEXEC') || die();
use Joomla\CMS\Filter\InputFilter;
/**
* Return folder browser results
*
* @deprecated
*/
class Browse extends AbstractTask
{
/**
* Execute the JSON API task
*
* @param array $parameters The parameters to this task
*
* @return mixed
*
* @throws \RuntimeException In case of an error
*/
public function execute(array $parameters = [])
{
throw new \RuntimeException('This method is no longer supported by the Akeeba Remote JSON API', 501);
}
}
<?php
/**
* @package akeebabackup
* @copyright Copyright (c)2006-2023 Nicholas K. Dionysopoulos / Akeeba Ltd
* @license GNU General Public License version 3, or later
*/
namespace Akeeba\Backup\Site\Model\Json\Task;
// Protect from unauthorized access
defined('_JEXEC') || die();
use Akeeba\Backup\Site\Model\Statistics;
use Akeeba\Engine\Platform;
/**
* Delete a backup record
*/
class Delete extends AbstractTask
{
/**
* Execute the JSON API task
*
* @param array $parameters The parameters to this task
*
* @return mixed
*
* @throws \RuntimeException In case of an error
*/
public function execute(array $parameters = array())
{
// Get the passed configuration values
$defConfig = array(
'backup_id' => 0,
);
$defConfig = array_merge($defConfig, $parameters);
$backup_id = (int)$defConfig['backup_id'];
/** @var Statistics $model */
$model = $this->container->factory->model('Statistics')->tmpInstance();
$model->setState('id', $backup_id);
try
{
$model->delete();
}
catch (\Exception $e)
{
throw new \RuntimeException($e->getMessage(), 500);
}
return true;
}
}
<?php
/**
* @package akeebabackup
* @copyright Copyright (c)2006-2023 Nicholas K. Dionysopoulos / Akeeba Ltd
* @license GNU General Public License version 3, or later
*/
namespace Akeeba\Backup\Site\Model\Json\Task;
// Protect from unauthorized access
defined('_JEXEC') || die();
use Akeeba\Backup\Site\Model\Statistics;
use Akeeba\Engine\Platform;
/**
* Delete the backup archives of a backup record
*/
class DeleteFiles extends AbstractTask
{
/**
* Execute the JSON API task
*
* @param array $parameters The parameters to this task
*
* @return mixed
*
* @throws \RuntimeException In case of an error
*/
public function execute(array $parameters = array())
{
// Get the passed configuration values
$defConfig = array(
'backup_id' => 0,
);
$defConfig = array_merge($defConfig, $parameters);
$backup_id = (int)$defConfig['backup_id'];
/** @var Statistics $model */
$model = $this->container->factory->model('Statistics')->tmpInstance();
$model->setState('id', $backup_id);
try
{
$model->deleteFile();
}
catch (\Exception $e)
{
throw new \RuntimeException($e->getMessage(), 500);
}
return true;
}
}
<?php
/**
* @package akeebabackup
* @copyright Copyright (c)2006-2023 Nicholas K. Dionysopoulos / Akeeba Ltd
* @license GNU General Public License version 3, or later
*/
namespace Akeeba\Backup\Site\Model\Json\Task;
// Protect from unauthorized access
defined('_JEXEC') || die();
/**
* Delete a backup profile
*
* @deprecated
*/
class DeleteProfile extends AbstractTask
{
/**
* Execute the JSON API task
*
* @param array $parameters The parameters to this task
*
* @return mixed
*
* @throws \RuntimeException In case of an error
*/
public function execute(array $parameters = array())
{
throw new \RuntimeException('This method is no longer supported by the Akeeba Remote JSON API', 501);
}
}
<?php
/**
* @package akeebabackup
* @copyright Copyright (c)2006-2023 Nicholas K. Dionysopoulos / Akeeba Ltd
* @license GNU General Public License version 3, or later
*/
namespace Akeeba\Backup\Site\Model\Json\Task;
// Protect from unauthorized access
defined('_JEXEC') || die();
use Akeeba\Engine\Factory;
use Akeeba\Engine\Platform;
/**
* Download a chunk of a backup archive over HTTP
*/
class Download extends AbstractTask
{
/**
* Execute the JSON API task
*
* @param array $parameters The parameters to this task
*
* @return mixed
*
* @throws \RuntimeException In case of an error
*/
public function execute(array $parameters = array())
{
// Get the passed configuration values
$defConfig = array(
'backup_id' => 0,
'part_id' => 1,
'segment' => 1,
'chunk_size' => 1
);
$defConfig = array_merge($defConfig, $parameters);
$backup_id = (int)$defConfig['backup_id'];
$part_id = (int)$defConfig['part_id'];
$segment = (int)$defConfig['segment'];
$chunk_size = (int)$defConfig['chunk_size'];
$backup_stats = Platform::getInstance()->get_statistics($backup_id);
if (empty($backup_stats))
{
// Backup record doesn't exist
throw new \RuntimeException('Invalid backup record identifier', 404);
}
$files = Factory::getStatistics()->get_all_filenames($backup_stats);
if ((($files === null ? 0 : count($files)) < $part_id) || ($part_id <= 0))
{
// Invalid part
throw new \RuntimeException('Invalid backup part', 404);
}
$file = $files[ $part_id - 1 ];
$filesize = @filesize($file);
$seekPos = $chunk_size * 1048576 * ($segment - 1);
if ($seekPos > $filesize)
{
// Trying to seek past end of file
throw new \RuntimeException('Invalid segment', 404);
}
$fp = fopen($file, 'r');
if ($fp === false)
{
// Could not read file
throw new \RuntimeException('Error reading backup archive', 500);
}
rewind($fp);
if (fseek($fp, $seekPos, SEEK_SET) === -1)
{
// Could not seek to position
throw new \RuntimeException('Error reading specified segment', 500);
}
$buffer = fread($fp, 1048576);
if ($buffer === false)
{
throw new \RuntimeException('Error reading specified segment', 500);
}
fclose($fp);
return base64_encode($buffer);
}
}
<?php
/**
* @package akeebabackup
* @copyright Copyright (c)2006-2023 Nicholas K. Dionysopoulos / Akeeba Ltd
* @license GNU General Public License version 3, or later
*/
namespace Akeeba\Backup\Site\Model\Json\Task;
// Protect from unauthorized access
defined('_JEXEC') || die();
use Akeeba\Engine\Factory;
use Akeeba\Engine\Platform;
/**
* Download an entire backup archive directly over HTTP
*/
class DownloadDirect extends AbstractTask
{
/**
* Execute the JSON API task
*
* @param array $parameters The parameters to this task
*
* @return mixed
*
* @throws \RuntimeException In case of an error
*/
public function execute(array $parameters = array())
{
// Get the passed configuration values
$defConfig = array(
'backup_id' => 0,
'part_id' => 1,
);
$defConfig = array_merge($defConfig, $parameters);
$backup_id = (int)$defConfig['backup_id'];
$part_id = (int)$defConfig['part_id'];
$backup_stats = Platform::getInstance()->get_statistics($backup_id);
if (empty($backup_stats))
{
// Backup record doesn't exist
@ob_end_clean();
header('HTTP/1.1 500 Invalid backup record identifier');
flush();
$this->container->platform->closeApplication();
}
$files = Factory::getStatistics()->get_all_filenames($backup_stats);
if ((($files === null ? 0 : count($files)) < $part_id) || ($part_id <= 0))
{
// Invalid part
@ob_end_clean();
header('HTTP/1.1 500 Invalid backup part');
flush();
$this->container->platform->closeApplication();
}
$filename = $files[ $part_id - 1 ];
@clearstatcache();
// For a certain unmentionable browser
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
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 file size, 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 = 1048576; //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();
$this->container->platform->closeApplication();
// Totally ignored, only added to make static analysis happy
return null;
}
}
<?php
/**
* @package akeebabackup
* @copyright Copyright (c)2006-2023 Nicholas K. Dionysopoulos / Akeeba Ltd
* @license GNU General Public License version 3, or later
*/
namespace Akeeba\Backup\Site\Model\Json\Task;
// Protect from unauthorized access
defined('_JEXEC') || die();
use Akeeba\Backup\Site\Model\Profiles;
use Akeeba\Engine\Factory;
/**
* Export the profile's configuration
*/
class ExportConfiguration extends AbstractTask
{
/**
* Execute the JSON API task
*
* @param array $parameters The parameters to this task
*
* @return mixed
*
* @throws \RuntimeException In case of an error
*/
public function execute(array $parameters = array())
{
// Get the passed configuration values
$defConfig = array(
'profile' => 0,
);
$defConfig = array_merge($defConfig, $parameters);
$profile_id = (int)$defConfig['profile'];
if ($profile_id <= 0)
{
$profile_id = 1;
}
/** @var Profiles $profile */
$profile = $this->container->factory->model('Profiles')->tmpInstance();
$data = $profile->findOrFail($profile_id)->getData();
if (substr($data['configuration'], 0, 12) == '###AES128###')
{
// Load the server key file if necessary
if (!defined('AKEEBA_SERVERKEY'))
{
$filename = JPATH_COMPONENT_ADMINISTRATOR . '/BackupEngine/serverkey.php';
include_once $filename;
}
$key = Factory::getSecureSettings()->getKey();
$data['configuration'] = Factory::getSecureSettings()->decryptSettings($data['configuration'], $key);
}
return array(
'description' => $data['description'],
'configuration' => $data['configuration'],
'filters' => $data['filters'],
);
}
}
<?php
/**
* @package akeebabackup
* @copyright Copyright (c)2006-2023 Nicholas K. Dionysopoulos / Akeeba Ltd
* @license GNU General Public License version 3, or later
*/
namespace Akeeba\Backup\Site\Model\Json\Task;
// Protect from unauthorized access
defined('_JEXEC') || die();
use Akeeba\Engine\Factory;
use Akeeba\Engine\Platform;
/**
* Get information for a given backup record
*/
class GetBackupInfo extends AbstractTask
{
/**
* Execute the JSON API task
*
* @param array $parameters The parameters to this task
*
* @return mixed
*
* @throws \RuntimeException In case of an error
*/
public function execute(array $parameters = array())
{
// Get the passed configuration values
$defConfig = array(
'backup_id' => 0,
);
$defConfig = array_merge($defConfig, $parameters);
$backup_id = (int)$defConfig['backup_id'];
// Get the basic statistics
$record = Platform::getInstance()->get_statistics($backup_id);
// Backup record doesn't exist
if (empty($record))
{
throw new \RuntimeException('Invalid backup record identifier', 404);
}
// Get a list of filenames
$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;
}
}
<?php
/**
* @package akeebabackup
* @copyright Copyright (c)2006-2023 Nicholas K. Dionysopoulos / Akeeba Ltd
* @license GNU General Public License version 3, or later
*/
namespace Akeeba\Backup\Site\Model\Json\Task;
// Protect from unauthorized access
defined('_JEXEC') || die();
use Joomla\CMS\Filter\InputFilter;
/**
* Get the database entities along with their filtering status (typically for rendering a GUI)
*
* @deprecated
*/
class GetDBEntities extends AbstractTask
{
/**
* Execute the JSON API task
*
* @param array $parameters The parameters to this task
*
* @return mixed
*
* @throws \RuntimeException In case of an error
*/
public function execute(array $parameters = [])
{
throw new \RuntimeException('This method is no longer supported by the Akeeba Remote JSON API', 501);
}
}