Unsecured FastPreview Implementation
parent
94516d183b
commit
dc23e3fbdb
|
@ -23,6 +23,7 @@ declare(strict_types=1);
|
|||
|
||||
namespace OCA\Memories\AppInfo;
|
||||
|
||||
use OCA\Memories\FastPreview;
|
||||
use OCA\Memories\Listeners\PostDeleteListener;
|
||||
use OCA\Memories\Listeners\PostWriteListener;
|
||||
use OCP\AppFramework\App;
|
||||
|
@ -67,6 +68,7 @@ class Application extends App implements IBootstrap
|
|||
|
||||
public function register(IRegistrationContext $context): void
|
||||
{
|
||||
FastPreview::intercept();
|
||||
$context->registerEventListener(NodeWrittenEvent::class, PostWriteListener::class);
|
||||
$context->registerEventListener(NodeTouchedEvent::class, PostWriteListener::class);
|
||||
$context->registerEventListener(NodeDeletedEvent::class, PostDeleteListener::class);
|
||||
|
|
|
@ -0,0 +1,184 @@
|
|||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace OCA\Memories;
|
||||
|
||||
class FastPreview
|
||||
{
|
||||
public static function intercept()
|
||||
{
|
||||
try {
|
||||
if (!\array_key_exists('fileId', $_GET) || !$_GET['fileId']) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Get configuration
|
||||
$config = \OC::$server->getConfig();
|
||||
$root = $config->getSystemValue('datadirectory', \OC::$SERVERROOT.'/data');
|
||||
$instanceId = $config->getSystemValue('instanceid', '');
|
||||
|
||||
// Get paths
|
||||
$appFolder = 'appdata_'.$instanceId.'/preview';
|
||||
$folderPath = \OC\Preview\Storage\Root::getInternalFolder($_GET['fileId']);
|
||||
$absFolderPath = "{$root}/{$appFolder}/{$folderPath}";
|
||||
|
||||
// Get preview specs
|
||||
$w = (int) $_GET['x'];
|
||||
$h = (int) $_GET['y'];
|
||||
$crop = '0' === $_GET['a'];
|
||||
$mode = 'fill';
|
||||
if (!$w || !$h) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Get max preview specs and extension
|
||||
[$maxWidth, $maxHeight, $ext] = self::getMaxPreview($absFolderPath);
|
||||
|
||||
// Get size of the preview we want
|
||||
[$w, $h] = self::calculateSize($w, $h, $crop, $mode, $maxWidth, $maxHeight);
|
||||
|
||||
// Construct filename of preview
|
||||
$filename = $w.'-'.$h;
|
||||
if ($w === $maxWidth && $h === $maxHeight) {
|
||||
$filename .= '-max';
|
||||
}
|
||||
$filename .= '.'.$ext;
|
||||
$absPath = "{$absFolderPath}/{$filename}";
|
||||
|
||||
// Check file
|
||||
if (!file_exists($absPath)) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Send file
|
||||
if ('jpg' === $ext || 'jpeg' === $ext) {
|
||||
header('Content-Type: image/jpeg');
|
||||
} elseif ('png' === $ext) {
|
||||
header('Content-Type: image/png');
|
||||
} elseif ('gif' === $ext) {
|
||||
header('Content-Type: image/gif');
|
||||
} else {
|
||||
return; // ?
|
||||
}
|
||||
|
||||
header('Content-Length: '.filesize($absPath));
|
||||
header('Content-Disposition: inline; filename="'.$filename.'"');
|
||||
header('Cache-Control: max-age=604800, private, immutable');
|
||||
header('X-Memories-FastPreview: HIT');
|
||||
header("Content-Security-Policy: default-src 'none';base-uri 'none';manifest-src 'self';frame-ancestors 'none'");
|
||||
readfile($absPath);
|
||||
|
||||
// send to user
|
||||
flush();
|
||||
ob_flush();
|
||||
|
||||
exit;
|
||||
} catch (\Exception $e) {
|
||||
}
|
||||
}
|
||||
|
||||
/** Get file with max preview and get size and extension */
|
||||
private static function getMaxPreview(string $absFolderPath)
|
||||
{
|
||||
$files = scandir($absFolderPath);
|
||||
foreach ($files as $file) {
|
||||
if (false !== strpos($file, '-max')) {
|
||||
$parts = explode('-', $file);
|
||||
if (3 !== \count($parts)) {
|
||||
continue;
|
||||
}
|
||||
$maxWidth = (int) $parts[0];
|
||||
$maxHeight = (int) $parts[1];
|
||||
|
||||
$extParts = explode('.', $parts[2]);
|
||||
if (2 !== \count($extParts)) {
|
||||
continue;
|
||||
}
|
||||
$ext = $extParts[1];
|
||||
|
||||
return [$maxWidth, $maxHeight, $ext];
|
||||
}
|
||||
}
|
||||
|
||||
throw new \Exception('No max preview found');
|
||||
}
|
||||
|
||||
// Taken from @nextcloud/server Generator.php
|
||||
private static function calculateSize($width, $height, $crop, $mode, $maxWidth, $maxHeight)
|
||||
{
|
||||
/*
|
||||
* If we are not cropping we have to make sure the requested image
|
||||
* respects the aspect ratio of the original.
|
||||
*/
|
||||
if (!$crop) {
|
||||
$ratio = $maxHeight / $maxWidth;
|
||||
|
||||
if (-1 === $width) {
|
||||
$width = $height / $ratio;
|
||||
}
|
||||
if (-1 === $height) {
|
||||
$height = $width * $ratio;
|
||||
}
|
||||
|
||||
$ratioH = $height / $maxHeight;
|
||||
$ratioW = $width / $maxWidth;
|
||||
|
||||
/*
|
||||
* Fill means that the $height and $width are the max
|
||||
* Cover means min.
|
||||
*/
|
||||
if ('fill' === $mode) {
|
||||
if ($ratioH > $ratioW) {
|
||||
$height = $width * $ratio;
|
||||
} else {
|
||||
$width = $height / $ratio;
|
||||
}
|
||||
} elseif ('cover' === $mode) {
|
||||
if ($ratioH > $ratioW) {
|
||||
$width = $height / $ratio;
|
||||
} else {
|
||||
$height = $width * $ratio;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if ($height !== $maxHeight && $width !== $maxWidth) {
|
||||
// Scale to the nearest power of four
|
||||
$pow4height = 4 ** ceil(log($height) / log(4));
|
||||
$pow4width = 4 ** ceil(log($width) / log(4));
|
||||
|
||||
// Minimum size is 64
|
||||
$pow4height = max($pow4height, 64);
|
||||
$pow4width = max($pow4width, 64);
|
||||
|
||||
$ratioH = $height / $pow4height;
|
||||
$ratioW = $width / $pow4width;
|
||||
|
||||
if ($ratioH < $ratioW) {
|
||||
$width = $pow4width;
|
||||
$height /= $ratioW;
|
||||
} else {
|
||||
$height = $pow4height;
|
||||
$width /= $ratioH;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Make sure the requested height and width fall within the max
|
||||
* of the preview.
|
||||
*/
|
||||
if ($height > $maxHeight) {
|
||||
$ratio = $height / $maxHeight;
|
||||
$height = $maxHeight;
|
||||
$width /= $ratio;
|
||||
}
|
||||
if ($width > $maxWidth) {
|
||||
$ratio = $width / $maxWidth;
|
||||
$width = $maxWidth;
|
||||
$height /= $ratio;
|
||||
}
|
||||
|
||||
return [(int) round($width), (int) round($height)];
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue