| Current Path : /home/zieirix/www/administrator/components/com_akeeba/BackupEngine/Util/ |
| Current File : /home/zieirix/www/administrator/components/com_akeeba/BackupEngine/Util/FactoryStorage.php |
<?php
/**
* Akeeba Engine
*
* @package akeebaengine
* @copyright Copyright (c)2006-2022 Nicholas K. Dionysopoulos / Akeeba Ltd
* @license GNU General Public License version 3, or later
*/
namespace Akeeba\Engine\Util;
defined('AKEEBAENGINE') || die();
use Akeeba\Engine\Factory;
use InvalidArgumentException;
use RuntimeException;
/**
* Management class for temporary storage of the serialised engine state.
*/
class FactoryStorage
{
protected static $tempFileStoragePath;
/**
* Returns the fully qualified path to the storage file
*
* @param string $tag Backup tag
* @param string $extension File extension, default is php
*
* @return string
*/
public function get_storage_filename($tag = null, $extension = 'php')
{
if (is_null(self::$tempFileStoragePath))
{
$registry = Factory::getConfiguration();
self::$tempFileStoragePath = $registry->get('akeeba.basic.output_directory', '');
if (empty(self::$tempFileStoragePath))
{
throw new InvalidArgumentException('You have not set a backup output directory.');
}
self::$tempFileStoragePath = rtrim(self::$tempFileStoragePath, '/\\');
if (!is_writable(self::$tempFileStoragePath) || !is_readable(self::$tempFileStoragePath))
{
throw new InvalidArgumentException(sprintf('Backup output directory %s needs to be both readable and writeable to PHP for this backup software to function correctly.', self::$tempFileStoragePath));
}
}
$tag = empty($tag) ? '' : $tag;
$filename = sprintf("akstorage%s%s.%s", empty($tag) ? '' : '_', $tag, $extension);
return self::$tempFileStoragePath . DIRECTORY_SEPARATOR . $filename;
}
/**
* Resets the storage. This method removes all stored values.
*
* @param null $tag
*
* @return bool True on success
*/
public function reset($tag = null)
{
$filename = $this->get_storage_filename($tag);
if (!is_file($filename) && !is_link($filename))
{
$filename = $this->get_storage_filename($tag, 'dat');
}
if (!is_file($filename) && !is_link($filename))
{
return false;
}
return @unlink($this->get_storage_filename($tag));
}
/**
* Stores a value to the storage
*
* @param string $value Serialised value to store
* @param string|null $tag Backup tag
* @param string $extension File extension to use, default is php
*
* @return bool True on success
*/
public function set($value, $tag = null, $extension = 'php')
{
$storage_filename = $this->get_storage_filename($tag, $extension);
if (file_exists($storage_filename))
{
@unlink($storage_filename);
}
$isPHPFile = strtolower($extension) == 'php';
return @file_put_contents($storage_filename, $this->encode($value, $isPHPFile)) !== false;
}
/**
* Retrieves a value from storage
*
* @param string|null $tag Backup tag. Used to determine the session state (memory) file name.
*
* @return false|string
*/
public function &get($tag = null)
{
$ret = false;
$storage_filename = $this->get_storage_filename($tag);
$isPHPFile = true;
$data = @file_get_contents($storage_filename);
/**
* Some hosts, like WPEngine, do not allow us to use .php files for storing the factory state. In these case we
* fall back to using the far less secure .dat extension. This if-block caters for that case.
*/
if ($data === false)
{
$storage_filename = $this->get_storage_filename($tag, 'dat');
$isPHPFile = false;
$data = @file_get_contents($storage_filename);
}
if ($data === false)
{
return $ret;
}
try
{
$ret = $this->decode($data, $isPHPFile);
}
catch (RuntimeException $e)
{
$ret = false;
}
unset($data);
return $ret;
}
/**
* Encodes the (serialized) data in a format suitable for storing in a deliberately web-inaccessible PHP file.
*
* IMPORTANT: On some hosts we HAVE to fall back to a .dat file. This is nowhere near as secure. This is not a
* problem with Akeeba Backup but with the host, e.g. WPEngine. We WANT to do things securely but hosts' misguided
* attempts at "security" force us to have a very insecure fallback. Please do not report this as a security issue
* with us. report it to the host. We can't do something the host doesn't allow our code to do, obviously!
*
* @param string $data The data to encode
* @param bool $isPHPFile Is this file extension .php?
*
* @return string The encoded data
*/
public function encode(&$data, $isPHPFile = true)
{
$encodingMethod = $this->getEncodingMethod();
switch ($encodingMethod)
{
case 'base64':
$ret = base64_encode($data);
break;
case 'uuencode':
$ret = convert_uuencode($data);
break;
case 'plain':
default:
$ret = $data;
break;
}
if ($isPHPFile)
{
return '<' . '?' . 'php die(); ' . '>' . '?' . "\n" .
$encodingMethod . "\n" . $ret;
}
return $encodingMethod . "\n" . $ret;
}
/**
* Decodes the data read from the deliberately web-inaccessible PHP file.
*
* @param string $data The data read from the file
* @param bool $isPHPFile Does the memory file have a .php extension?
*
* @return false|string The decoded data. False if the decoding failed.
*/
public function decode(&$data, $isPHPFile = true)
{
// Parts: 0 = PHP die line; 1 = encoding mode; 2 = data
$parts = explode("\n", $data, 3);
$expectedPartsCount = $isPHPFile ? 3 : 2;
if (count($parts) != $expectedPartsCount)
{
throw new RuntimeException("Invalid backup temporary data (memory file)");
}
$encodingIndex = $isPHPFile ? 1 : 0;
$dataIndex = $isPHPFile ? 2 : 1;
switch ($parts[$encodingIndex])
{
case 'base64';
return base64_decode($parts[$dataIndex]);
break;
case 'uuencode':
return convert_uudecode($parts[$dataIndex]);
break;
case 'plain':
return $parts[$dataIndex];
break;
default:
throw new RuntimeException(sprintf('Unsupported encoding method “%s”', $parts[$encodingIndex]));
break;
}
}
/**
* Get the recommended method for encoding the temporary data
*
* @return string
*/
protected function getEncodingMethod()
{
// Preferred encoding: base sixty four, handled by PHP
if (function_exists('base64_encode') && function_exists('base64_decode'))
{
return 'base64';
}
// Fallback: UUencoding
if (function_exists('convert_uuencode') && function_exists('convert_uudecode'))
{
return 'uuencode';
}
// Final fallback (should NOT be necessary): plain text encoding
return 'plain';
}
}