154 lines
4.8 KiB
PHP
154 lines
4.8 KiB
PHP
<?php
|
|
|
|
/**
|
|
* Simple file upload layer. Handles file metadata and storage
|
|
* FileHandler: part that handles a specific disk storage layout
|
|
*
|
|
* Version 2019-05-01
|
|
* (c) Vitaliy Filippov 2018+
|
|
*/
|
|
|
|
class FileHandler
|
|
{
|
|
public static $uploadCurl;
|
|
|
|
public function __construct($config)
|
|
{
|
|
$this->config = $config + [
|
|
'basedir' => '',
|
|
'baseurl' => '',
|
|
'mime_blacklist' => '#'.
|
|
// HTML may contain cookie-stealing JavaScript and web bugs
|
|
'^text/(html|(x-)?javascript)$|^application/x-shellscript$'.
|
|
// PHP/Perl/Bash/etc scripts may execute arbitrary code on the server
|
|
'|php|perl|python|bash|x-c?sh(e|$)'.
|
|
// Client-side hazards on Internet Explorer
|
|
'|^text/scriptlet$|^application/x-msdownload$'.
|
|
// Windows metafile, client-side vulnerability on some systems
|
|
'|^application/x-msmetafile$'.
|
|
'#is',
|
|
'thumb_path' => 'thumb/',
|
|
];
|
|
}
|
|
|
|
public function getPath($web_or_fs, $file)
|
|
{
|
|
$s = $file['sha1'];
|
|
return $this->config[$web_or_fs ? 'baseurl' : 'basedir'] . '/' .
|
|
substr($s, 0, 1) . '/' . substr($s, 0, 2) . '/' . $s . '.' . $file['format'];
|
|
}
|
|
|
|
public function getThumbPath($web_or_fs, $file, $type)
|
|
{
|
|
$s = $file['sha1'];
|
|
$ext = $file['format'] == 'jpg' || $file['format'] == 'png' || $file['format'] == 'gif'
|
|
? $file['format'] : 'jpg';
|
|
return $this->config[$web_or_fs ? 'baseurl' : 'basedir'] . '/' .
|
|
$this->config['thumb_path'] . '/' . $type . '/' .substr($s, 0, 1) . '/' . substr($s, 0, 2) . '/' . $s . '.' . $ext;
|
|
}
|
|
|
|
public function upload($localFile, $allowedFormats = FileUtils::ANYTHING)
|
|
{
|
|
$tmp_name = $localFile->getLocalPath();
|
|
$props = FileUtils::getProps($allowedFormats, $this->config['mime_blacklist'], $tmp_name, $localFile->getFileName());
|
|
$fn = $this->getPath(false, $props);
|
|
if (file_exists($fn) && filesize($fn) == $props['size'])
|
|
{
|
|
return $props;
|
|
}
|
|
FileUtils::mkpath(dirname($fn), true);
|
|
$m = $localFile->shouldMove ? 'rename' : 'copy';
|
|
if (!@$m($tmp_name, $fn))
|
|
{
|
|
$error = error_get_last();
|
|
throw new Exception($error['message']);
|
|
}
|
|
chmod($fn, 0666 & ~umask());
|
|
return $props;
|
|
}
|
|
|
|
public function uploadUrl($url, $flags = FileUtils::ONLY_IMAGES, $curl_options = [])
|
|
{
|
|
if (!$url)
|
|
{
|
|
return NULL;
|
|
}
|
|
$file = NULL;
|
|
if (substr($url, 0, 2) == '//')
|
|
{
|
|
$url = "http:$url";
|
|
}
|
|
if (!self::$uploadCurl)
|
|
{
|
|
// Reuse handle to use keepalive when possible
|
|
self::$uploadCurl = curl_init();
|
|
}
|
|
curl_setopt_array(self::$uploadCurl, [
|
|
CURLOPT_URL => $url,
|
|
CURLOPT_RETURNTRANSFER => true,
|
|
] + $curl_options);
|
|
$s = curl_exec(self::$uploadCurl);
|
|
if ($s)
|
|
{
|
|
$tmp = tempnam(sys_get_temp_dir(), 'upl');
|
|
file_put_contents($tmp, $s);
|
|
unset($s);
|
|
$file = $this->upload(new LocalFile($tmp, true), $flags);
|
|
@unlink($tmp);
|
|
}
|
|
else
|
|
{
|
|
// Log it as E_USER_NOTICE
|
|
trigger_error(curl_error(self::$uploadCurl));
|
|
}
|
|
return $file;
|
|
}
|
|
|
|
public function deleteFile($e)
|
|
{
|
|
$disk_name = $this->getPath(false, $e);
|
|
if (file_exists($disk_name))
|
|
{
|
|
// Remove old file
|
|
unlink($disk_name);
|
|
}
|
|
$thumb_glob = $this->getThumbPath(false, $e, '*');
|
|
foreach (glob($thumb_glob) as $thumb)
|
|
{
|
|
if (file_exists($thumb))
|
|
{
|
|
unlink($thumb);
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Get or generate a thumbnail and return its URL
|
|
*/
|
|
public function getThumb($file, $width, $height, $force = false, $crop = false, $alignY = 0.5)
|
|
{
|
|
$type = FileUtils::getThumbType($file, $width, $height, $crop, $alignY);
|
|
if (!$type)
|
|
{
|
|
return NULL;
|
|
}
|
|
$fn = $this->getThumbPath(false, $file, $type['type']);
|
|
if (!file_exists($fn) || $force)
|
|
{
|
|
if (substr($file['mimetype'], 0, 6) === 'video/')
|
|
{
|
|
$sourcefn = $this->getThumbPath(false, $file, 'src');
|
|
}
|
|
else
|
|
{
|
|
$sourcefn = $this->getPath(false, $file);
|
|
}
|
|
if (!FileUtils::generateThumbnail($sourcefn, $fn, $file, $type['width'], $type['height'], $crop, $alignY))
|
|
{
|
|
return NULL;
|
|
}
|
|
}
|
|
return $this->getThumbPath(true, $file, $type);
|
|
}
|
|
}
|