2018-11-21 09:38:54 +01:00
|
|
|
<?php
|
|
|
|
/**
|
|
|
|
* @file src/Model/Storage/Filesystem.php
|
|
|
|
* @brief Storage backend system
|
|
|
|
*/
|
|
|
|
|
|
|
|
namespace Friendica\Model\Storage;
|
|
|
|
|
|
|
|
use Friendica\Core\Config;
|
|
|
|
use Friendica\Core\L10n;
|
|
|
|
use Friendica\Core\Logger;
|
|
|
|
use Friendica\Util\Strings;
|
|
|
|
|
|
|
|
/**
|
|
|
|
* @brief Filesystem based storage backend
|
|
|
|
*
|
|
|
|
* This class manage data on filesystem.
|
|
|
|
* Base folder for storage is set in storage.filesystem_path.
|
|
|
|
* Best would be for storage folder to be outside webserver folder, we are using a
|
|
|
|
* folder relative to code tree root as default to ease things for users in shared hostings.
|
|
|
|
* Each new resource gets a value as reference and is saved in a
|
|
|
|
* folder tree stucture created from that value.
|
|
|
|
*/
|
|
|
|
class Filesystem implements IStorage
|
|
|
|
{
|
|
|
|
// Default base folder
|
2018-12-08 18:49:16 +01:00
|
|
|
const DEFAULT_BASE_FOLDER = 'storage';
|
2018-11-21 09:38:54 +01:00
|
|
|
|
|
|
|
private static function getBasePath()
|
|
|
|
{
|
2019-01-29 12:36:23 +01:00
|
|
|
$path = Config::get('storage', 'filesystem_path', self::DEFAULT_BASE_FOLDER);
|
|
|
|
return rtrim($path, '/');
|
2018-11-21 09:38:54 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* @brief Split data ref and return file path
|
|
|
|
* @param string $ref Data reference
|
|
|
|
* @return string
|
|
|
|
*/
|
|
|
|
private static function pathForRef($ref)
|
|
|
|
{
|
|
|
|
$base = self::getBasePath();
|
2018-11-21 15:10:47 +01:00
|
|
|
$fold1 = substr($ref, 0, 2);
|
|
|
|
$fold2 = substr($ref, 2, 2);
|
|
|
|
$file = substr($ref, 4);
|
2018-11-21 09:38:54 +01:00
|
|
|
|
2018-12-08 18:49:16 +01:00
|
|
|
return implode('/', [$base, $fold1, $fold2, $file]);
|
2018-11-21 09:38:54 +01:00
|
|
|
}
|
|
|
|
|
2018-11-21 16:36:11 +01:00
|
|
|
|
|
|
|
/**
|
|
|
|
* @brief Create dirctory tree to store file, with .htaccess and index.html files
|
2019-01-06 22:06:53 +01:00
|
|
|
* @param string $file Path and filename
|
|
|
|
* @throws StorageException
|
2018-11-21 16:36:11 +01:00
|
|
|
*/
|
|
|
|
private static function createFoldersForFile($file)
|
|
|
|
{
|
|
|
|
$path = dirname($file);
|
|
|
|
|
|
|
|
if (!is_dir($path)) {
|
|
|
|
if (!mkdir($path, 0770, true)) {
|
2018-12-08 18:49:16 +01:00
|
|
|
Logger::log('Failed to create dirs ' . $path);
|
|
|
|
throw new StorageException(L10n::t('Filesystem storage failed to create "%s". Check you write permissions.', $path));
|
2018-11-21 16:36:11 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
$base = self::getBasePath();
|
2018-11-29 08:34:00 +01:00
|
|
|
|
2018-11-21 16:36:11 +01:00
|
|
|
while ($path !== $base) {
|
2018-12-08 18:49:16 +01:00
|
|
|
if (!is_file($path . '/index.html')) {
|
|
|
|
file_put_contents($path . '/index.html', '');
|
2018-11-21 16:36:11 +01:00
|
|
|
}
|
2019-01-29 12:35:30 +01:00
|
|
|
chmod($path . '/index.html', 0660);
|
|
|
|
chmod($path, 0770);
|
2018-11-21 16:36:11 +01:00
|
|
|
$path = dirname($path);
|
|
|
|
}
|
2018-12-08 18:49:16 +01:00
|
|
|
if (!is_file($path . '/index.html')) {
|
|
|
|
file_put_contents($path . '/index.html', '');
|
2019-01-29 12:35:30 +01:00
|
|
|
chmod($path . '/index.html', 0660);
|
2018-11-21 16:36:11 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-11-21 09:38:54 +01:00
|
|
|
public static function get($ref)
|
|
|
|
{
|
|
|
|
$file = self::pathForRef($ref);
|
2018-11-21 15:10:47 +01:00
|
|
|
if (!is_file($file)) {
|
2018-12-08 18:49:16 +01:00
|
|
|
return '';
|
2018-11-21 15:10:47 +01:00
|
|
|
}
|
2018-11-21 09:38:54 +01:00
|
|
|
|
|
|
|
return file_get_contents($file);
|
|
|
|
}
|
|
|
|
|
2018-12-08 18:49:16 +01:00
|
|
|
public static function put($data, $ref = '')
|
2018-11-21 09:38:54 +01:00
|
|
|
{
|
2018-12-08 18:49:16 +01:00
|
|
|
if ($ref === '') {
|
2018-11-21 09:38:54 +01:00
|
|
|
$ref = Strings::getRandomHex();
|
|
|
|
}
|
|
|
|
$file = self::pathForRef($ref);
|
|
|
|
|
2018-11-21 16:36:11 +01:00
|
|
|
self::createFoldersForFile($file);
|
2018-11-21 09:38:54 +01:00
|
|
|
|
|
|
|
$r = file_put_contents($file, $data);
|
|
|
|
if ($r === FALSE) {
|
2018-12-08 18:49:16 +01:00
|
|
|
Logger::log('Failed to write data to ' . $file);
|
|
|
|
throw new StorageException(L10n::t('Filesystem storage failed to save data to "%s". Check your write permissions', $file));
|
2018-11-21 09:38:54 +01:00
|
|
|
}
|
2019-01-29 12:35:30 +01:00
|
|
|
chmod($file, 0660);
|
2018-11-21 09:38:54 +01:00
|
|
|
return $ref;
|
|
|
|
}
|
|
|
|
|
|
|
|
public static function delete($ref)
|
|
|
|
{
|
|
|
|
$file = self::pathForRef($ref);
|
2018-11-21 15:10:47 +01:00
|
|
|
// return true if file doesn't exists. we want to delete it: success with zero work!
|
2018-11-29 08:34:00 +01:00
|
|
|
if (!is_file($file)) {
|
2018-11-21 15:10:47 +01:00
|
|
|
return true;
|
|
|
|
}
|
2018-11-21 09:38:54 +01:00
|
|
|
return unlink($file);
|
|
|
|
}
|
|
|
|
|
2018-12-08 18:49:16 +01:00
|
|
|
public static function getOptions()
|
|
|
|
{
|
|
|
|
return [
|
|
|
|
'storagepath' => [
|
|
|
|
'input',
|
|
|
|
L10n::t('Storage base path'),
|
|
|
|
self::getBasePath(),
|
2019-02-09 08:42:55 +01:00
|
|
|
L10n::t('Folder where uploaded files are saved. For maximum security, This should be a path outside web server folder tree')
|
2018-12-08 18:49:16 +01:00
|
|
|
]
|
|
|
|
];
|
|
|
|
}
|
|
|
|
|
|
|
|
public static function saveOptions($data)
|
|
|
|
{
|
2019-10-16 14:35:14 +02:00
|
|
|
$storagepath = $data['storagepath'] ?? '';
|
2018-12-08 18:49:16 +01:00
|
|
|
if ($storagepath === '' || !is_dir($storagepath)) {
|
|
|
|
return [
|
|
|
|
'storagepath' => L10n::t('Enter a valid existing folder')
|
|
|
|
];
|
|
|
|
};
|
|
|
|
Config::set('storage', 'filesystem_path', $storagepath);
|
|
|
|
return [];
|
|
|
|
}
|
|
|
|
|
2018-11-21 15:10:47 +01:00
|
|
|
}
|