2021-07-04 00:01:35 +02:00
|
|
|
<?php
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Simple PHP upload class
|
|
|
|
*
|
|
|
|
* Adapted from aivis/PHP-file-upload-class
|
|
|
|
*
|
|
|
|
* @description File upload class for RaspAP
|
|
|
|
* @author Bill Zimmerman <billzimmerman@gmail.com>
|
|
|
|
* @author Aivis Silins
|
|
|
|
* @link https://github.com/aivis/PHP-file-upload-class
|
|
|
|
* @license https://github.com/raspap/raspap-webgui/blob/master/LICENSE
|
|
|
|
*/
|
|
|
|
|
2021-07-04 12:16:21 +02:00
|
|
|
namespace RaspAP\Uploader;
|
|
|
|
|
2021-07-04 00:01:35 +02:00
|
|
|
class Upload
|
|
|
|
{
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Default directory persmissions (destination)
|
|
|
|
*/
|
|
|
|
protected $default_permissions = 0750;
|
|
|
|
|
|
|
|
/**
|
|
|
|
* File post array
|
|
|
|
*
|
|
|
|
* @var array
|
|
|
|
*/
|
|
|
|
protected $file_post = array();
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Destination directory
|
|
|
|
*
|
|
|
|
* @var string
|
|
|
|
*/
|
|
|
|
protected $destination;
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Fileinfo
|
|
|
|
*
|
|
|
|
* @var object
|
|
|
|
*/
|
|
|
|
protected $finfo;
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Data about file
|
|
|
|
*
|
|
|
|
* @var array
|
|
|
|
*/
|
|
|
|
public $file = array();
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Max. file size
|
|
|
|
*
|
|
|
|
* @var int
|
|
|
|
*/
|
|
|
|
protected $max_file_size;
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Allowed mime types
|
|
|
|
*
|
|
|
|
* @var array
|
|
|
|
*/
|
|
|
|
protected $mimes = array();
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Temp path
|
|
|
|
*
|
|
|
|
* @var string
|
|
|
|
*/
|
|
|
|
protected $tmp_name;
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Validation errors
|
|
|
|
*
|
|
|
|
* @var array
|
|
|
|
*/
|
|
|
|
protected $validation_errors = array();
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Filename (new)
|
|
|
|
*
|
|
|
|
* @var string
|
|
|
|
*/
|
|
|
|
protected $filename;
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Internal callbacks (filesize check, mime, etc)
|
|
|
|
*
|
|
|
|
* @var array
|
|
|
|
*/
|
|
|
|
private $callbacks = array();
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Root dir
|
|
|
|
*
|
|
|
|
* @var string
|
|
|
|
*/
|
|
|
|
protected $root;
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Return upload object
|
|
|
|
*
|
|
|
|
* $destination = 'path/to/file/destination/';
|
|
|
|
*
|
|
|
|
* @param string $destination
|
|
|
|
* @param string $root
|
|
|
|
* @return Upload
|
|
|
|
*/
|
|
|
|
public static function factory($destination, $root = false)
|
|
|
|
{
|
|
|
|
return new Upload($destination, $root);
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Define root constant and set & create destination path
|
|
|
|
*
|
|
|
|
* @param string $destination
|
|
|
|
* @param string $root
|
|
|
|
*/
|
|
|
|
public function __construct($destination, $root = false)
|
|
|
|
{
|
|
|
|
if ($root) {
|
|
|
|
$this->root = $root;
|
|
|
|
} else {
|
|
|
|
$this->root = $_SERVER['DOCUMENT_ROOT'] . DIRECTORY_SEPARATOR;
|
|
|
|
}
|
|
|
|
|
|
|
|
// set & create destination path
|
|
|
|
if (!$this->set_destination($destination)) {
|
|
|
|
throw new Exception('Upload: Unable to create destination. '.$this->root . $this->destination);
|
|
|
|
}
|
|
|
|
//create finfo object
|
2021-07-04 12:16:21 +02:00
|
|
|
$this->finfo = new \finfo();
|
2021-07-04 00:01:35 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Set target filename
|
|
|
|
*
|
|
|
|
* @param string $filename
|
|
|
|
*/
|
|
|
|
public function set_filename($filename)
|
|
|
|
{
|
|
|
|
$this->filename = $filename;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Check & Save file
|
|
|
|
*
|
|
|
|
* Return data about current upload
|
|
|
|
*
|
|
|
|
* @return array
|
|
|
|
*/
|
|
|
|
public function upload($filename = false)
|
|
|
|
{
|
|
|
|
if($filename ) {
|
|
|
|
$this->set_filename($filename);
|
|
|
|
}
|
|
|
|
|
|
|
|
$this->set_filename($filename);
|
|
|
|
|
|
|
|
if ($this->check()) {
|
|
|
|
$this->save();
|
|
|
|
}
|
|
|
|
|
|
|
|
// return state data
|
|
|
|
return $this->get_state();
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Save file on server
|
|
|
|
* Return state data
|
|
|
|
*
|
|
|
|
* @return array
|
|
|
|
*/
|
|
|
|
public function save()
|
|
|
|
{
|
|
|
|
$this->save_file();
|
|
|
|
return $this->get_state();
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Validate file (execute callbacks)
|
|
|
|
* Returns TRUE if validation successful
|
|
|
|
*
|
|
|
|
* @return bool
|
|
|
|
*/
|
|
|
|
public function check()
|
|
|
|
{
|
|
|
|
//execute callbacks (check filesize, mime, also external callbacks
|
|
|
|
$this->validate();
|
|
|
|
|
|
|
|
//add error messages
|
|
|
|
$this->file['errors'] = $this->get_errors();
|
|
|
|
|
|
|
|
//change file validation status
|
|
|
|
$this->file['status'] = empty($this->validation_errors);
|
|
|
|
|
|
|
|
return $this->file['status'];
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Get current state data
|
|
|
|
*
|
|
|
|
* @return array
|
|
|
|
*/
|
|
|
|
public function get_state()
|
|
|
|
{
|
|
|
|
return $this->file;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Save file on server
|
|
|
|
*/
|
|
|
|
protected function save_file()
|
|
|
|
{
|
|
|
|
//create & set new filename
|
|
|
|
if(empty($this->filename)) {
|
|
|
|
$this->create_new_filename();
|
|
|
|
}
|
|
|
|
|
|
|
|
//set filename
|
|
|
|
$this->file['filename'] = $this->filename;
|
|
|
|
|
|
|
|
//set full path
|
|
|
|
$this->file['full_path'] = $this->root . $this->destination . $this->filename;
|
|
|
|
$this->file['path'] = $this->destination . $this->filename;
|
|
|
|
|
|
|
|
$status = move_uploaded_file($this->tmp_name, $this->file['full_path']);
|
|
|
|
|
|
|
|
//checks whether upload successful
|
|
|
|
if (!$status) {
|
|
|
|
throw new Exception('Upload: Failed to upload file.');
|
|
|
|
}
|
|
|
|
|
|
|
|
//done
|
|
|
|
$this->file['status'] = true;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Set data about file
|
|
|
|
*/
|
|
|
|
protected function set_file_data()
|
|
|
|
{
|
|
|
|
$file_size = $this->get_file_size();
|
|
|
|
$this->file = array(
|
|
|
|
'status' => false,
|
|
|
|
'destination' => $this->destination,
|
|
|
|
'size_in_bytes' => $file_size,
|
|
|
|
'size_in_mb' => $this->bytes_to_mb($file_size),
|
|
|
|
'mime' => $this->get_file_mime(),
|
|
|
|
'filename' => $this->file_post['name'],
|
|
|
|
'tmp_name' => $this->file_post['tmp_name'],
|
|
|
|
'post_data' => $this->file_post,
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Set validation error
|
|
|
|
*
|
|
|
|
* @param string $message
|
|
|
|
*/
|
|
|
|
public function set_error($message)
|
|
|
|
{
|
|
|
|
$this->validation_errors[] = $message;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Return validation errors
|
|
|
|
*
|
|
|
|
* @return array
|
|
|
|
*/
|
|
|
|
public function get_errors()
|
|
|
|
{
|
|
|
|
return $this->validation_errors;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Set external callback methods
|
|
|
|
*
|
|
|
|
* @param object $instance_of_callback_object
|
|
|
|
* @param array $callback_methods
|
|
|
|
*/
|
|
|
|
public function callbacks($instance_of_callback_object, $callback_methods)
|
|
|
|
{
|
|
|
|
if (empty($instance_of_callback_object)) {
|
|
|
|
throw new Exception('Upload: $instance_of_callback_object cannot be empty.');
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!is_array($callback_methods)) {
|
|
|
|
throw new Exception('Upload: $callback_methods data type need to be array.');
|
|
|
|
}
|
|
|
|
|
|
|
|
$this->external_callback_object = $instance_of_callback_object;
|
|
|
|
$this->external_callback_methods = $callback_methods;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Execute callbacks
|
|
|
|
*/
|
|
|
|
protected function validate()
|
|
|
|
{
|
|
|
|
//get curent errors
|
|
|
|
$errors = $this->get_errors();
|
|
|
|
|
|
|
|
if (empty($errors)) {
|
|
|
|
|
|
|
|
//set data about current file
|
|
|
|
$this->set_file_data();
|
|
|
|
|
|
|
|
//execute internal callbacks
|
|
|
|
$this->execute_callbacks($this->callbacks, $this);
|
|
|
|
|
|
|
|
//execute external callbacks
|
|
|
|
$this->execute_callbacks($this->external_callback_methods, $this->external_callback_object);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Execute callbacks
|
|
|
|
*/
|
|
|
|
protected function execute_callbacks($callbacks, $object)
|
|
|
|
{
|
|
|
|
foreach($callbacks as $method) {
|
|
|
|
$object->$method($this);
|
|
|
|
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* File mime type validation callback
|
|
|
|
*
|
|
|
|
* @param object $object
|
|
|
|
*/
|
|
|
|
protected function check_mime_type($object)
|
|
|
|
{
|
|
|
|
if (!empty($object->mimes)) {
|
|
|
|
if (!in_array($object->file['mime'], $object->mimes)) {
|
2022-01-01 01:35:15 +01:00
|
|
|
$object->set_error('MIME type not allowed.');
|
2021-07-04 00:01:35 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Set allowed mime types
|
|
|
|
*
|
|
|
|
* @param array $mimes
|
|
|
|
*/
|
|
|
|
public function set_allowed_mime_types($mimes)
|
|
|
|
{
|
|
|
|
$this->mimes = $mimes;
|
|
|
|
//if mime types is set -> set callback
|
|
|
|
$this->callbacks[] = 'check_mime_type';
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* File size validation callback
|
|
|
|
*
|
|
|
|
* @param object $object
|
|
|
|
*/
|
|
|
|
protected function check_file_size($object)
|
|
|
|
{
|
|
|
|
if (!empty($object->max_file_size)) {
|
|
|
|
$file_size_in_mb = $this->bytes_to_mb($object->file['size_in_bytes']);
|
|
|
|
if ($object->max_file_size <= $file_size_in_mb) {
|
|
|
|
$object->set_error('File exceeds maximum allowed size.');
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Set max file size
|
|
|
|
*
|
|
|
|
* @param int $size
|
|
|
|
*/
|
|
|
|
public function set_max_file_size($size)
|
|
|
|
{
|
|
|
|
$this->max_file_size = $size;
|
|
|
|
|
|
|
|
//if max file size is set -> set callback
|
|
|
|
$this->callbacks[] = 'check_file_size';
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Set File array to object
|
|
|
|
*
|
|
|
|
* @param array $file
|
|
|
|
*/
|
|
|
|
public function file($file)
|
|
|
|
{
|
|
|
|
$this->set_file_array($file);
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Set file array
|
|
|
|
*
|
|
|
|
* @param array $file
|
|
|
|
*/
|
|
|
|
protected function set_file_array($file)
|
|
|
|
{
|
|
|
|
//checks whether file array is valid
|
|
|
|
if (!$this->check_file_array($file)) {
|
|
|
|
//file not selected or some bigger problems (broken files array)
|
|
|
|
$this->set_error('Please select file.');
|
|
|
|
}
|
|
|
|
|
|
|
|
//set file data
|
|
|
|
$this->file_post = $file;
|
|
|
|
|
|
|
|
//set tmp path
|
|
|
|
$this->tmp_name = $file['tmp_name'];
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Checks whether Files post array is valid
|
|
|
|
*
|
|
|
|
* @return bool
|
|
|
|
*/
|
|
|
|
protected function check_file_array($file)
|
|
|
|
{
|
|
|
|
return isset($file['error'])
|
|
|
|
&& !empty($file['name'])
|
|
|
|
&& !empty($file['type'])
|
|
|
|
&& !empty($file['tmp_name'])
|
|
|
|
&& !empty($file['size']);
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Get file mime type
|
|
|
|
*
|
|
|
|
* @return string
|
|
|
|
*/
|
|
|
|
protected function get_file_mime()
|
|
|
|
{
|
|
|
|
return $this->finfo->file($this->tmp_name, FILEINFO_MIME_TYPE);
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Get file size
|
|
|
|
*
|
|
|
|
* @return int
|
|
|
|
*/
|
|
|
|
protected function get_file_size()
|
|
|
|
{
|
|
|
|
return filesize($this->tmp_name);
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Set destination path (return TRUE on success)
|
|
|
|
*
|
|
|
|
* @param string $destination
|
|
|
|
* @return bool
|
|
|
|
*/
|
|
|
|
protected function set_destination($destination)
|
|
|
|
{
|
|
|
|
$this->destination = $destination . DIRECTORY_SEPARATOR;
|
|
|
|
return $this->destination_exist() ? true : $this->create_destination();
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Checks whether destination folder exists
|
|
|
|
*
|
|
|
|
* @return bool
|
|
|
|
*/
|
|
|
|
protected function destination_exist()
|
|
|
|
{
|
|
|
|
return is_writable($this->root . $this->destination);
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Create path to destination
|
|
|
|
*
|
|
|
|
* @param string $dir
|
|
|
|
* @return bool
|
|
|
|
*/
|
|
|
|
protected function create_destination()
|
|
|
|
{
|
|
|
|
return mkdir($this->root . $this->destination, $this->default_permissions, true);
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Set unique filename
|
|
|
|
*
|
|
|
|
* @return string
|
|
|
|
*/
|
|
|
|
protected function create_new_filename()
|
|
|
|
{
|
|
|
|
$filename = sha1(mt_rand(1, 9999) . $this->destination . uniqid()) . time();
|
|
|
|
$this->set_filename($filename);
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Convert bytes to MB
|
|
|
|
*
|
|
|
|
* @param int $bytes
|
|
|
|
* @return int
|
|
|
|
*/
|
|
|
|
protected function bytes_to_mb($bytes)
|
|
|
|
{
|
|
|
|
return round(($bytes / 1048576), 2);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|