tss
parent
cb83b2b98c
commit
ec8469333e
|
@ -6,7 +6,7 @@
|
||||||
http://chevereto.com/
|
http://chevereto.com/
|
||||||
|
|
||||||
@author Rodolfo Berrios A. <http://rodolfoberrios.com/>
|
@author Rodolfo Berrios A. <http://rodolfoberrios.com/>
|
||||||
<inbox@rodolfoberrios.com>
|
<inbox@rodolfoberrios.com>
|
||||||
|
|
||||||
Copyright (C) Rodolfo Berrios A. All rights reserved.
|
Copyright (C) Rodolfo Berrios A. All rights reserved.
|
||||||
|
|
||||||
|
@ -16,449 +16,476 @@
|
||||||
--------------------------------------------------------------------- */
|
--------------------------------------------------------------------- */
|
||||||
|
|
||||||
namespace CHV;
|
namespace CHV;
|
||||||
use G, Exception;
|
|
||||||
|
|
||||||
class Upload {
|
use G;
|
||||||
// filename => name.ext
|
use Exception;
|
||||||
// file => /full/path/to/name.ext
|
|
||||||
// name => name
|
|
||||||
|
|
||||||
public $source;
|
class Upload
|
||||||
public $uploaded;
|
{
|
||||||
|
// filename => name.ext
|
||||||
|
// file => /full/path/to/name.ext
|
||||||
|
// name => name
|
||||||
|
|
||||||
// Sets the type of resource being uploaded
|
public $source;
|
||||||
public function setType($type) {
|
public $uploaded;
|
||||||
$this->type = $type;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Set source
|
// Sets the type of resource being uploaded
|
||||||
public function setSource($source) {
|
public function setType($type)
|
||||||
$this->source = $source;
|
{
|
||||||
$this->type = G\is_url($this->source) ? 'url' : 'file';
|
$this->type = $type;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Set destination
|
// Set source
|
||||||
public function setDestination($destination) {
|
public function setSource($source)
|
||||||
$this->destination = G\forward_slash($destination);
|
{
|
||||||
}
|
$this->source = $source;
|
||||||
|
$this->type = G\is_url($this->source) ? 'url' : 'file';
|
||||||
|
}
|
||||||
|
|
||||||
// Set storage
|
// Set destination
|
||||||
public function setStorageId($storage_id) {
|
public function setDestination($destination)
|
||||||
$this->storage_id = is_numeric($storage_id) ? $storage_id : NULL;
|
{
|
||||||
}
|
$this->destination = G\forward_slash($destination);
|
||||||
|
}
|
||||||
|
|
||||||
// Set file basename
|
// Set storage
|
||||||
public function setFilename($name) {
|
public function setStorageId($storage_id)
|
||||||
$this->name = $name;
|
{
|
||||||
}
|
$this->storage_id = is_numeric($storage_id) ? $storage_id : null;
|
||||||
|
}
|
||||||
|
|
||||||
// Set options
|
// Set file basename
|
||||||
public function setOptions($options) {
|
public function setFilename($name)
|
||||||
$this->options = $options;
|
{
|
||||||
}
|
$this->name = $name;
|
||||||
|
}
|
||||||
|
|
||||||
// Set individual option
|
// Set options
|
||||||
public function setOption($key, $value) {
|
public function setOptions($options)
|
||||||
$this->options[$key] = $value;
|
{
|
||||||
}
|
$this->options = $options;
|
||||||
|
}
|
||||||
|
|
||||||
// Default options
|
// Set individual option
|
||||||
public static function getDefaultOptions() {
|
public function setOption($key, $value)
|
||||||
return array(
|
{
|
||||||
'max_size' => G\get_bytes('2 MB'), // it should be 'max_filesize'
|
$this->options[$key] = $value;
|
||||||
'filenaming' => 'original',
|
}
|
||||||
'exif' => TRUE,
|
|
||||||
'allowed_formats' => self::getAvailableImageFormats(), // array
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
// Default options
|
||||||
* Do the thing
|
public static function getDefaultOptions()
|
||||||
* @Exeption 4xx
|
{
|
||||||
*/
|
return array(
|
||||||
public function exec() {
|
'max_size' => G\get_bytes('2 MB'), // it should be 'max_filesize'
|
||||||
|
'filenaming' => 'original',
|
||||||
|
'exif' => true,
|
||||||
|
'allowed_formats' => self::getAvailableImageFormats(), // array
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
// Merge options
|
/**
|
||||||
$this->options = array_merge(self::getDefaultOptions(), (array) $this->options);
|
* Do the thing
|
||||||
|
* @Exeption 4xx
|
||||||
|
*/
|
||||||
|
public function exec()
|
||||||
|
{
|
||||||
|
|
||||||
$this->validateInput(); // Exception 1
|
// Merge options
|
||||||
|
$this->options = array_merge(self::getDefaultOptions(), (array) $this->options);
|
||||||
|
|
||||||
$this->fetchSource(); // Exception 2
|
$this->validateInput(); // Exception 1
|
||||||
|
|
||||||
$this->validateSourceFile(); // Exception 3
|
$this->fetchSource(); // Exception 2
|
||||||
|
|
||||||
if(!is_array($this->options['allowed_formats'])) {
|
$this->validateSourceFile(); // Exception 3
|
||||||
$this->options['allowed_formats'] = explode(',', $this->options['allowed_formats']);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Save the source name
|
if (!is_array($this->options['allowed_formats'])) {
|
||||||
$this->source_name = G\get_filename_without_extension($this->type == "url" ? $this->source : $this->source["name"]);
|
$this->options['allowed_formats'] = explode(',', $this->options['allowed_formats']);
|
||||||
|
}
|
||||||
|
|
||||||
// Set file extension
|
// Save the source name
|
||||||
$this->extension = $this->source_image_fileinfo["extension"];
|
$this->source_name = G\get_filename_without_extension($this->type == "url" ? $this->getNameFromURL($this->source) : $this->source["name"]);
|
||||||
|
|
||||||
// Workaround the $name
|
// Set file extension
|
||||||
if(!$this->name) {
|
$this->extension = $this->source_image_fileinfo["extension"];
|
||||||
$this->name = $this->source_name;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Fix conflicting starting dots (some Apache installs)
|
// Workaround the $name
|
||||||
$this->name = ltrim($this->name, '.');
|
if (!$this->name) {
|
||||||
|
$this->name = $this->source_name;
|
||||||
|
}
|
||||||
|
|
||||||
// Fix file extension
|
// Fix conflicting starting dots (some Apache installs)
|
||||||
if(G\get_file_extension($this->name) == $this->extension) {
|
$this->name = ltrim($this->name, '.');
|
||||||
$this->name = G\get_filename_without_extension($this->name);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Set the fixed filename
|
// Fix file extension
|
||||||
$this->fixed_filename = preg_replace('/(.*)\.(th|md|original|lg)\.([\w]+)$/', '$1.$3', $this->name . '.' . $this->extension);
|
if (G\get_file_extension($this->name) == $this->extension) {
|
||||||
|
$this->name = G\get_filename_without_extension($this->name);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Set the fixed filename
|
||||||
|
$this->fixed_filename = preg_replace('/(.*)\.(th|md|original|lg)\.([\w]+)$/', '$1.$3', $this->name . '.' . $this->extension);
|
||||||
|
|
||||||
// Workaround for JPEG Exif data
|
// Workaround for JPEG Exif data
|
||||||
if($this->extension == 'jpg' and array_key_exists('exif', $this->options)) {
|
if ($this->extension == 'jpg' and array_key_exists('exif', $this->options)) {
|
||||||
$this->source_image_exif = NULL;
|
$this->source_image_exif = null;
|
||||||
if($this->options['exif']) {
|
if ($this->options['exif']) {
|
||||||
// Fetch JPEG Exif data (when available)
|
// Fetch JPEG Exif data (when available)
|
||||||
if(function_exists('exif_read_data')) {
|
if (function_exists('exif_read_data')) {
|
||||||
$this->source_image_exif = @exif_read_data($this->downstream);
|
$this->source_image_exif = @exif_read_data($this->downstream);
|
||||||
if($this->source_image_exif) {
|
if ($this->source_image_exif) {
|
||||||
$this->source_image_exif['FileName'] = $this->source_filename;
|
$this->source_image_exif['FileName'] = $this->source_filename;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
// Remove JPEG Exif data
|
// Remove JPEG Exif data
|
||||||
$img = imagecreatefromjpeg($this->downstream);
|
$img = imagecreatefromjpeg($this->downstream);
|
||||||
if($img) {
|
if ($img) {
|
||||||
imagejpeg($img, $this->downstream, 90);
|
imagejpeg($img, $this->downstream, 90);
|
||||||
imagedestroy($img);
|
imagedestroy($img);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Set uploaded_file
|
* Set uploaded_file
|
||||||
* Local storage uploads will be allocated at the target destination
|
* Local storage uploads will be allocated at the target destination
|
||||||
* External storage will be allocated to the temp directory
|
* External storage will be allocated to the temp directory
|
||||||
*/
|
*/
|
||||||
$this->uploaded_file = G\name_unique_file($this->destination, $this->options['filenaming'], $this->fixed_filename);
|
$this->uploaded_file = G\name_unique_file($this->destination, $this->options['filenaming'], $this->fixed_filename);
|
||||||
|
|
||||||
$this->source = [
|
$this->source = [
|
||||||
'filename' => $this->source_filename, // file.ext
|
'filename' => $this->source_filename, // file.ext
|
||||||
'name' => $this->source_name, // file
|
'name' => $this->source_name, // file
|
||||||
'image_exif' => $this->source_image_exif, // exif-data array
|
'image_exif' => $this->source_image_exif, // exif-data array
|
||||||
'fileinfo' => G\get_image_fileinfo($this->downstream), // fileinfo array
|
'fileinfo' => G\get_image_fileinfo($this->downstream), // fileinfo array
|
||||||
];
|
];
|
||||||
|
|
||||||
// Fix image orientation
|
// Fix image orientation
|
||||||
if($this->source_image_exif and $this->source_image_exif["Orientation"]) {
|
if ($this->source_image_exif and $this->source_image_exif["Orientation"]) {
|
||||||
$this->fixImageOrientation($this->downstream, $this->source_image_exif);
|
$this->fixImageOrientation($this->downstream, $this->source_image_exif);
|
||||||
}
|
}
|
||||||
|
|
||||||
$uploaded = @rename($this->downstream, $this->uploaded_file);
|
$uploaded = @rename($this->downstream, $this->uploaded_file);
|
||||||
@unlink($this->downstream);
|
@unlink($this->downstream);
|
||||||
|
|
||||||
if(file_exists($this->downstream)) {
|
if (file_exists($this->downstream)) {
|
||||||
error_log("Warning: temp file " . $this->downstream . "wasn't removed.");
|
error_log("Warning: temp file " . $this->downstream . "wasn't removed.");
|
||||||
}
|
}
|
||||||
|
|
||||||
if(!$uploaded) {
|
if (!$uploaded) {
|
||||||
throw new UploadException("Can't move temp file to its destination", 400);
|
throw new UploadException("Can't move temp file to its destination", 400);
|
||||||
}
|
}
|
||||||
|
|
||||||
// For some PHP environments
|
// For some PHP environments
|
||||||
if(!$this->storage_id) {
|
if (!$this->storage_id) {
|
||||||
@chmod($this->uploaded_file, 0644);
|
@chmod($this->uploaded_file, 0644);
|
||||||
}
|
}
|
||||||
|
|
||||||
$this->uploaded = array(
|
$this->uploaded = array(
|
||||||
'file' => $this->uploaded_file,
|
'file' => $this->uploaded_file,
|
||||||
'filename' => G\get_filename($this->uploaded_file),
|
'filename' => G\get_filename($this->uploaded_file),
|
||||||
'name' => G\get_filename_without_extension($this->uploaded_file),
|
'name' => G\get_filename_without_extension($this->uploaded_file),
|
||||||
'fileinfo' => G\get_image_fileinfo($this->uploaded_file)
|
'fileinfo' => G\get_image_fileinfo($this->uploaded_file)
|
||||||
);
|
);
|
||||||
|
}
|
||||||
|
|
||||||
}
|
// Get available (supported) extensions
|
||||||
|
public static function getAvailableImageFormats()
|
||||||
|
{
|
||||||
|
$formats = Settings::get('upload_available_image_formats');
|
||||||
|
return explode(',', $formats);
|
||||||
|
}
|
||||||
|
|
||||||
// Get available (supported) extensions
|
//remove query string from url to get correct image name
|
||||||
public static function getAvailableImageFormats() {
|
protected function getNameFromURL()
|
||||||
$formats = Settings::get('upload_available_image_formats');
|
{
|
||||||
return explode(',', $formats);
|
if (strpos($this->source, '?')) {
|
||||||
}
|
return substr($this->source, 0, strpos($this->source, '?'));
|
||||||
|
} else {
|
||||||
|
return $this->source;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Failover since v3.8.12
|
// Failover since v3.8.12
|
||||||
public static function getEnabledImageFormats() {
|
public static function getEnabledImageFormats()
|
||||||
return Image::getEnabledImageFormats();
|
{
|
||||||
}
|
return Image::getEnabledImageFormats();
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* validate_input aka "first stage validation"
|
* validate_input aka "first stage validation"
|
||||||
* This checks for valid input source data
|
* This checks for valid input source data
|
||||||
* @Exception 1XX
|
* @Exception 1XX
|
||||||
*/
|
*/
|
||||||
protected function validateInput() {
|
protected function validateInput()
|
||||||
|
{
|
||||||
|
$check_missing = ["type", "source", "destination"];
|
||||||
|
missing_values_to_exception($this, "CHV\UploadException", $check_missing, 100);
|
||||||
|
|
||||||
$check_missing = ["type", "source", "destination"];
|
// Validate $type
|
||||||
missing_values_to_exception($this, "CHV\UploadException", $check_missing, 100);
|
if (!preg_match("/^(url|file)$/", $this->type)) {
|
||||||
|
throw new UploadException('Invalid $type "'.$this->type.'"', 110);
|
||||||
|
}
|
||||||
|
|
||||||
// Validate $type
|
// Handle flood
|
||||||
if(!preg_match("/^(url|file)$/", $this->type)) {
|
$flood = self::handleFlood();
|
||||||
throw new UploadException('Invalid $type "'.$this->type.'"', 110);
|
if ($flood) {
|
||||||
}
|
throw new UploadException(strtr('Flood detected. You can only upload %limit% images per %time%', ['%limit%' => $flood['limit'], '%time%' => $flood['by']]), 130);
|
||||||
|
}
|
||||||
|
|
||||||
// Handle flood
|
// Validate $source
|
||||||
$flood = self::handleFlood();
|
if ($this->type == 'file') {
|
||||||
if($flood) {
|
if (count($this->source) < 5) { // Valid $_FILES ?
|
||||||
throw new UploadException(strtr('Flood detected. You can only upload %limit% images per %time%', ['%limit%' => $flood['limit'], '%time%' => $flood['by']]), 130);
|
throw new UploadException("Invalid file source", 120);
|
||||||
}
|
}
|
||||||
|
} elseif ($this->type == "url") {
|
||||||
|
if (!G\is_image_url($this->source) && !G\is_url($this->source)) {
|
||||||
|
throw new UploadException("Invalid image URL", 122);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Validate $source
|
// Validate $destination
|
||||||
if($this->type == 'file') {
|
if (!is_dir($this->destination)) { // Try to create the missing directory
|
||||||
if(count($this->source) < 5) { // Valid $_FILES ?
|
|
||||||
throw new UploadException("Invalid file source", 120);
|
|
||||||
}
|
|
||||||
} else if($this->type == "url") {
|
|
||||||
if(!G\is_image_url($this->source) && !G\is_url($this->source)) {
|
|
||||||
throw new UploadException("Invalid image URL", 122);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Validate $destination
|
$base_dir = G\add_ending_slash(G_ROOT_PATH . explode('/', preg_replace('#'.G_ROOT_PATH.'#', '', $this->destination, 1))[0]);
|
||||||
if(!is_dir($this->destination)) { // Try to create the missing directory
|
$base_perms = fileperms($base_dir);
|
||||||
|
|
||||||
$base_dir = G\add_ending_slash(G_ROOT_PATH . explode('/', preg_replace('#'.G_ROOT_PATH.'#', '', $this->destination, 1))[0]);
|
$old_umask = umask(0);
|
||||||
$base_perms = fileperms($base_dir);
|
$make_destination = mkdir($this->destination, $base_perms, true);
|
||||||
|
chmod($this->destination, $base_perms);
|
||||||
|
umask($old_umask);
|
||||||
|
|
||||||
$old_umask = umask(0);
|
if (!$make_destination) {
|
||||||
$make_destination = mkdir($this->destination, $base_perms, true);
|
throw new UploadException('$destination '.$this->destination.' is not a dir', 130);
|
||||||
chmod($this->destination, $base_perms);
|
}
|
||||||
umask($old_umask);
|
}
|
||||||
|
|
||||||
if(!$make_destination) {
|
// Can read $destination dir?
|
||||||
throw new UploadException('$destination '.$this->destination.' is not a dir', 130);
|
if (!is_readable($this->destination)) {
|
||||||
}
|
throw new UploadException("Can't read target destination dir", 131);
|
||||||
|
}
|
||||||
|
|
||||||
}
|
// Can write $destination dir?
|
||||||
|
if (!is_writable($this->destination)) {
|
||||||
|
throw new UploadException("Can't write target destination dir", 132);
|
||||||
|
}
|
||||||
|
|
||||||
// Can read $destination dir?
|
// Fix $destination trailing
|
||||||
if(!is_readable($this->destination)) {
|
$this->destination = G\add_ending_slash($this->destination);
|
||||||
throw new UploadException("Can't read target destination dir", 131);
|
}
|
||||||
}
|
|
||||||
|
|
||||||
// Can write $destination dir?
|
/**
|
||||||
if(!is_writable($this->destination)) {
|
* Fetch the $source file
|
||||||
throw new UploadException("Can't write target destination dir", 132);
|
* @Exception 2XX
|
||||||
}
|
*/
|
||||||
|
protected function fetchSource()
|
||||||
|
{
|
||||||
|
|
||||||
// Fix $destination trailing
|
// Set the downstream file
|
||||||
$this->destination = G\add_ending_slash($this->destination);
|
$this->downstream = @tempnam(sys_get_temp_dir(), 'chvtemp');
|
||||||
|
|
||||||
}
|
if (!$this->downstream || !@is_writable($this->downstream)) {
|
||||||
|
$this->downstream = @tempnam($this->destination, 'chvtemp');
|
||||||
|
if (!$this->downstream) {
|
||||||
|
throw new UploadException("Can't get a tempnam", 200);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
if ($this->type == 'file') {
|
||||||
* Fetch the $source file
|
if ($this->source['error'] !== UPLOAD_ERR_OK) {
|
||||||
* @Exception 2XX
|
switch ($this->source['error']) {
|
||||||
*/
|
case UPLOAD_ERR_INI_SIZE: // 1
|
||||||
protected function fetchSource() {
|
throw new UploadException('File too big', 201);
|
||||||
|
break;
|
||||||
|
case UPLOAD_ERR_FORM_SIZE: // 2
|
||||||
|
throw new UploadException('File exceeds form max size', 201);
|
||||||
|
break;
|
||||||
|
case UPLOAD_ERR_PARTIAL: // 3
|
||||||
|
throw new UploadException('File was partially uploaded', 201);
|
||||||
|
break;
|
||||||
|
case UPLOAD_ERR_NO_FILE: // 4
|
||||||
|
throw new UploadException('No file was uploaded', 201);
|
||||||
|
break;
|
||||||
|
case UPLOAD_ERR_NO_TMP_DIR: // 5
|
||||||
|
throw new UploadException('Missing temp folder', 201);
|
||||||
|
break;
|
||||||
|
case UPLOAD_ERR_CANT_WRITE: // 6
|
||||||
|
throw new UploadException('System write error', 201);
|
||||||
|
break;
|
||||||
|
case UPLOAD_ERR_EXTENSION: // 7
|
||||||
|
throw new UploadException('The upload was stopped', 201);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Set the downstream file
|
if (!@rename($this->source['tmp_name'], $this->downstream)) {
|
||||||
$this->downstream = @tempnam(sys_get_temp_dir(), 'chvtemp');
|
throw new UploadException("Can't move temp file to the target upload dir", 203);
|
||||||
|
}
|
||||||
|
} elseif ($this->type == "url") {
|
||||||
|
try {
|
||||||
|
G\fetch_url($this->source, $this->downstream);
|
||||||
|
} catch (Exception $e) {
|
||||||
|
throw new UploadException($e->getMessage(), 202);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if(!$this->downstream || !@is_writable($this->downstream)) {
|
$this->source_filename = basename($this->type == "file" ? $this->source["name"] : $this->source);
|
||||||
$this->downstream = @tempnam($this->destination, 'chvtemp');
|
}
|
||||||
if(!$this->downstream) {
|
|
||||||
throw new UploadException("Can't get a tempnam", 200);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if($this->type == 'file') {
|
protected function fixImageOrientation($image_filename, $exif)
|
||||||
|
{
|
||||||
|
if ($exif['Orientation'] == 1) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
switch ($this->extension) {
|
||||||
|
case 'jpg':
|
||||||
|
$image = imagecreatefromjpeg($image_filename);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
switch ($exif['Orientation']) {
|
||||||
|
case 3:
|
||||||
|
$image = imagerotate($image, 180, 0);
|
||||||
|
break;
|
||||||
|
case 6:
|
||||||
|
$image = imagerotate($image, -90, 0);
|
||||||
|
break;
|
||||||
|
case 8:
|
||||||
|
$image = imagerotate($image, 90, 0);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
imagejpeg($image, $image_filename, 90);
|
||||||
|
}
|
||||||
|
|
||||||
if($this->source['error'] !== UPLOAD_ERR_OK) {
|
/**
|
||||||
|
* validate_source_file aka "second stage validation"
|
||||||
|
* This checks for valid input source data
|
||||||
|
* @Exception 3XX
|
||||||
|
*/
|
||||||
|
protected function validateSourceFile()
|
||||||
|
{
|
||||||
|
|
||||||
switch($this->source['error']) {
|
// Nothing to do here
|
||||||
case UPLOAD_ERR_INI_SIZE: // 1
|
if (!file_exists($this->downstream)) {
|
||||||
throw new UploadException('File too big', 201);
|
throw new UploadException("Can't fetch target upload source (downstream)", 300);
|
||||||
break;
|
}
|
||||||
case UPLOAD_ERR_FORM_SIZE: // 2
|
|
||||||
throw new UploadException('File exceeds form max size', 201);
|
|
||||||
break;
|
|
||||||
case UPLOAD_ERR_PARTIAL: // 3
|
|
||||||
throw new UploadException('File was partially uploaded', 201);
|
|
||||||
break;
|
|
||||||
case UPLOAD_ERR_NO_FILE: // 4
|
|
||||||
throw new UploadException('No file was uploaded', 201);
|
|
||||||
break;
|
|
||||||
case UPLOAD_ERR_NO_TMP_DIR: // 5
|
|
||||||
throw new UploadException('Missing temp folder', 201);
|
|
||||||
break;
|
|
||||||
case UPLOAD_ERR_CANT_WRITE: // 6
|
|
||||||
throw new UploadException('System write error', 201);
|
|
||||||
break;
|
|
||||||
case UPLOAD_ERR_EXTENSION: // 7
|
|
||||||
throw new UploadException('The upload was stopped', 201);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
$this->source_image_fileinfo = G\get_image_fileinfo($this->downstream);
|
||||||
|
|
||||||
if(!@rename($this->source['tmp_name'], $this->downstream)) {
|
// file info?
|
||||||
throw new UploadException("Can't move temp file to the target upload dir", 203);
|
if (!$this->source_image_fileinfo) {
|
||||||
}
|
throw new UploadException("Can't get target upload source info", 310);
|
||||||
|
}
|
||||||
|
|
||||||
} else if($this->type == "url") {
|
// Valid image fileinto?
|
||||||
try {
|
if ($this->source_image_fileinfo['width'] == '' || $this->source_image_fileinfo['height'] == '') {
|
||||||
G\fetch_url($this->source, $this->downstream);
|
throw new UploadException("Invalid image", 311);
|
||||||
} catch(Exception $e) {
|
}
|
||||||
throw new UploadException($e->getMessage(), 202);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
$this->source_filename = basename($this->type == "file" ? $this->source["name"] : $this->source);
|
// Available image format?
|
||||||
|
if (!in_array($this->source_image_fileinfo['extension'], self::getAvailableImageFormats())) {
|
||||||
|
throw new UploadException("Unavailable image format", 313);
|
||||||
|
}
|
||||||
|
|
||||||
}
|
// Enabled image format?
|
||||||
|
if (!in_array($this->source_image_fileinfo['extension'], $this->options['allowed_formats'])) {
|
||||||
|
throw new UploadException(sprintf("Disabled image format (%s)", $this->source_image_fileinfo['extension']), 314);
|
||||||
|
}
|
||||||
|
|
||||||
protected function fixImageOrientation($image_filename, $exif) {
|
// Mime
|
||||||
if($exif['Orientation'] == 1) return;
|
if (!$this->isValidImageMime($this->source_image_fileinfo["mime"])) {
|
||||||
switch($this->extension) {
|
throw new UploadException("Invalid image mimetype", 312);
|
||||||
case 'jpg':
|
}
|
||||||
$image = imagecreatefromjpeg($image_filename);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
switch($exif['Orientation']) {
|
|
||||||
case 3:
|
|
||||||
$image = imagerotate($image, 180, 0);
|
|
||||||
break;
|
|
||||||
case 6:
|
|
||||||
$image = imagerotate($image, -90, 0);
|
|
||||||
break;
|
|
||||||
case 8:
|
|
||||||
$image = imagerotate($image, 90, 0);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
imagejpeg($image, $image_filename, 90);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
// Size
|
||||||
* validate_source_file aka "second stage validation"
|
if (!$this->options['max_size']) {
|
||||||
* This checks for valid input source data
|
$this->options['max_size'] = self::getDefaultOptions()['max_size'];
|
||||||
* @Exception 3XX
|
}
|
||||||
*/
|
if ($this->source_image_fileinfo["size"] > $this->options["max_size"]) {
|
||||||
protected function validateSourceFile() {
|
throw new UploadException("File too big - max " . G\format_bytes($this->options["max_size"]), 313);
|
||||||
|
}
|
||||||
|
|
||||||
// Nothing to do here
|
// BMP?
|
||||||
if(!file_exists($this->downstream)) {
|
if ($this->source_image_fileinfo['extension'] == 'bmp') {
|
||||||
throw new UploadException("Can't fetch target upload source (downstream)", 300);
|
$this->ImageConvert = new ImageConvert($this->downstream, "png", $this->downstream);
|
||||||
}
|
$this->downstream = $this->ImageConvert->out;
|
||||||
|
$this->source_image_fileinfo = G\get_image_fileinfo($this->downstream);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
$this->source_image_fileinfo = G\get_image_fileinfo($this->downstream);
|
// Handle flood uploads
|
||||||
|
protected static function handleFlood()
|
||||||
|
{
|
||||||
|
$logged_user = Login::getUser();
|
||||||
|
|
||||||
// file info?
|
if (!getSetting('flood_uploads_protection') || $logged_user['is_admin']) {
|
||||||
if(!$this->source_image_fileinfo) {
|
return false;
|
||||||
throw new UploadException("Can't get target upload source info", 310);
|
}
|
||||||
}
|
|
||||||
|
|
||||||
// Valid image fileinto?
|
$flood_limit = [];
|
||||||
if($this->source_image_fileinfo['width'] == '' || $this->source_image_fileinfo['height'] == '') {
|
foreach (['minute', 'hour', 'day', 'week', 'month'] as $v) {
|
||||||
throw new UploadException("Invalid image", 311);
|
$flood_limit[$v] = getSetting('flood_uploads_' . $v);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Available image format?
|
try {
|
||||||
if(!in_array($this->source_image_fileinfo['extension'], self::getAvailableImageFormats())) {
|
$db = DB::getInstance();
|
||||||
throw new UploadException("Unavailable image format", 313);
|
$flood_db = $db->queryFetchSingle(
|
||||||
}
|
"SELECT
|
||||||
|
|
||||||
// Enabled image format?
|
|
||||||
if(!in_array($this->source_image_fileinfo['extension'], $this->options['allowed_formats'])) {
|
|
||||||
throw new UploadException(sprintf("Disabled image format (%s)", $this->source_image_fileinfo['extension']), 314);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Mime
|
|
||||||
if(!$this->isValidImageMime($this->source_image_fileinfo["mime"])) {
|
|
||||||
throw new UploadException("Invalid image mimetype", 312);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Size
|
|
||||||
if(!$this->options['max_size']) {
|
|
||||||
$this->options['max_size'] = self::getDefaultOptions()['max_size'];
|
|
||||||
}
|
|
||||||
if($this->source_image_fileinfo["size"] > $this->options["max_size"]) {
|
|
||||||
throw new UploadException("File too big - max " . G\format_bytes($this->options["max_size"]), 313);
|
|
||||||
}
|
|
||||||
|
|
||||||
// BMP?
|
|
||||||
if($this->source_image_fileinfo['extension'] == 'bmp') {
|
|
||||||
$this->ImageConvert = new ImageConvert($this->downstream, "png", $this->downstream);
|
|
||||||
$this->downstream = $this->ImageConvert->out;
|
|
||||||
$this->source_image_fileinfo = G\get_image_fileinfo($this->downstream);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
// Handle flood uploads
|
|
||||||
protected static function handleFlood() {
|
|
||||||
|
|
||||||
$logged_user = Login::getUser();
|
|
||||||
|
|
||||||
if(!getSetting('flood_uploads_protection') || $logged_user['is_admin']) {
|
|
||||||
return FALSE;
|
|
||||||
}
|
|
||||||
|
|
||||||
$flood_limit = [];
|
|
||||||
foreach(['minute', 'hour', 'day', 'week', 'month'] as $v) {
|
|
||||||
$flood_limit[$v] = getSetting('flood_uploads_' . $v);
|
|
||||||
}
|
|
||||||
|
|
||||||
try {
|
|
||||||
$db = DB::getInstance();
|
|
||||||
$flood_db = $db->queryFetchSingle(
|
|
||||||
"SELECT
|
|
||||||
COUNT(IF(image_date_gmt >= DATE_SUB(UTC_TIMESTAMP(), INTERVAL 1 MINUTE), 1, NULL)) AS minute,
|
COUNT(IF(image_date_gmt >= DATE_SUB(UTC_TIMESTAMP(), INTERVAL 1 MINUTE), 1, NULL)) AS minute,
|
||||||
COUNT(IF(image_date_gmt >= DATE_SUB(UTC_TIMESTAMP(), INTERVAL 1 HOUR), 1, NULL)) AS hour,
|
COUNT(IF(image_date_gmt >= DATE_SUB(UTC_TIMESTAMP(), INTERVAL 1 HOUR), 1, NULL)) AS hour,
|
||||||
COUNT(IF(image_date_gmt >= DATE_SUB(UTC_TIMESTAMP(), INTERVAL 1 DAY), 1, NULL)) AS day,
|
COUNT(IF(image_date_gmt >= DATE_SUB(UTC_TIMESTAMP(), INTERVAL 1 DAY), 1, NULL)) AS day,
|
||||||
COUNT(IF(image_date_gmt >= DATE_SUB(UTC_TIMESTAMP(), INTERVAL 1 WEEK), 1, NULL)) AS week,
|
COUNT(IF(image_date_gmt >= DATE_SUB(UTC_TIMESTAMP(), INTERVAL 1 WEEK), 1, NULL)) AS week,
|
||||||
COUNT(IF(image_date_gmt >= DATE_SUB(UTC_TIMESTAMP(), INTERVAL 1 MONTH), 1, NULL)) AS month
|
COUNT(IF(image_date_gmt >= DATE_SUB(UTC_TIMESTAMP(), INTERVAL 1 MONTH), 1, NULL)) AS month
|
||||||
FROM ".DB::getTable('images')." WHERE image_uploader_ip='".G\get_client_ip()."' AND image_date_gmt >= DATE_SUB(UTC_TIMESTAMP(), INTERVAL 1 MONTH)");
|
FROM ".DB::getTable('images')." WHERE image_uploader_ip='".G\get_client_ip()."' AND image_date_gmt >= DATE_SUB(UTC_TIMESTAMP(), INTERVAL 1 MONTH)"
|
||||||
} catch(Exception $e) {} // Silence
|
);
|
||||||
|
} catch (Exception $e) {
|
||||||
|
} // Silence
|
||||||
|
|
||||||
$is_flood = FALSE;
|
$is_flood = false;
|
||||||
$flood_by = '';
|
$flood_by = '';
|
||||||
foreach(['minute', 'hour', 'day', 'week', 'month'] as $v) {
|
foreach (['minute', 'hour', 'day', 'week', 'month'] as $v) {
|
||||||
if($flood_limit[$v] > 0 and $flood_db[$v] >= $flood_limit[$v]) {
|
if ($flood_limit[$v] > 0 and $flood_db[$v] >= $flood_limit[$v]) {
|
||||||
$flood_by = $v;
|
$flood_by = $v;
|
||||||
$is_flood = TRUE;
|
$is_flood = true;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if($is_flood) {
|
if ($is_flood) {
|
||||||
if(getSetting('flood_uploads_notify') and !$_SESSION['flood_uploads_notify'][$flood_by]) {
|
if (getSetting('flood_uploads_notify') and !$_SESSION['flood_uploads_notify'][$flood_by]) {
|
||||||
try {
|
try {
|
||||||
$message = strtr('Flooding IP <a href="'.G\get_base_url('search/images/?q=ip:%ip').'">%ip</a>', ['%ip' => G\get_client_ip()]) . '<br>';
|
$message = strtr('Flooding IP <a href="'.G\get_base_url('search/images/?q=ip:%ip').'">%ip</a>', ['%ip' => G\get_client_ip()]) . '<br>';
|
||||||
if($logged_user) {
|
if ($logged_user) {
|
||||||
$message .= 'User <a href="'.$logged_user['url'].'">'.$logged_user['name'].'</a><br>';
|
$message .= 'User <a href="'.$logged_user['url'].'">'.$logged_user['name'].'</a><br>';
|
||||||
}
|
}
|
||||||
$message .= '<br>';
|
$message .= '<br>';
|
||||||
$message .= '<b>Uploads per time period</b>'."<br>";
|
$message .= '<b>Uploads per time period</b>'."<br>";
|
||||||
$message .= 'Minute: '.$flood_db['minute']."<br>";
|
$message .= 'Minute: '.$flood_db['minute']."<br>";
|
||||||
$message .= 'Hour: '.$flood_db['hour']."<br>";
|
$message .= 'Hour: '.$flood_db['hour']."<br>";
|
||||||
$message .= 'Week: '.$flood_db['day']."<br>";
|
$message .= 'Week: '.$flood_db['day']."<br>";
|
||||||
$message .= 'Month: '.$flood_db['week']."<br>";
|
$message .= 'Month: '.$flood_db['week']."<br>";
|
||||||
system_notification_email(['subject' => 'Flood report IP '. G\get_client_ip(), 'message' => $message]);
|
system_notification_email(['subject' => 'Flood report IP '. G\get_client_ip(), 'message' => $message]);
|
||||||
$_SESSION['flood_uploads_notify'][$flood_by] = true;
|
$_SESSION['flood_uploads_notify'][$flood_by] = true;
|
||||||
} catch(Exception $e) {} // Silence
|
} catch (Exception $e) {
|
||||||
}
|
} // Silence
|
||||||
|
}
|
||||||
|
|
||||||
return ['flood' => TRUE, 'limit' => $flood_limit[$flood_by], 'count' => $flood_db[$flood_by], 'by' => $flood_by];
|
return ['flood' => true, 'limit' => $flood_limit[$flood_by], 'count' => $flood_db[$flood_by], 'by' => $flood_by];
|
||||||
}
|
}
|
||||||
|
|
||||||
return FALSE;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
protected function isValidImageMime($mime) {
|
protected function isValidImageMime($mime)
|
||||||
return preg_match("@image/(gif|pjpeg|jpeg|png|x-png|bmp|x-ms-bmp|x-windows-bmp)$@", $mime);
|
{
|
||||||
}
|
return preg_match("@image/(gif|pjpeg|jpeg|png|x-png|bmp|x-ms-bmp|x-windows-bmp)$@", $mime);
|
||||||
|
}
|
||||||
|
|
||||||
protected function isValidNamingOption($string) {
|
protected function isValidNamingOption($string)
|
||||||
return in_array($string, array("mixed", "random", "original"));
|
{
|
||||||
}
|
return in_array($string, array("mixed", "random", "original"));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class UploadException extends Exception {}
|
class UploadException extends Exception
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
File diff suppressed because it is too large
Load Diff
Loading…
Reference in New Issue