Skip to content

Instantly share code, notes, and snippets.

@JimBecher
Created February 7, 2016 18:36
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save JimBecher/59cf97d0ddb1c526f25d to your computer and use it in GitHub Desktop.
Save JimBecher/59cf97d0ddb1c526f25d to your computer and use it in GitHub Desktop.
Uploader.php - Magento Permission adjustment to fix errors with uploaded images
<?php
/**
* Magento
*
* NOTICE OF LICENSE
*
* This source file is subject to the Open Software License (OSL 3.0)
* that is bundled with this package in the file LICENSE.txt.
* It is also available through the world-wide-web at this URL:
* http://opensource.org/licenses/osl-3.0.php
* If you did not receive a copy of the license and are unable to
* obtain it through the world-wide-web, please send an email
* to license@magento.com so we can send you a copy immediately.
*
* DISCLAIMER
*
* Do not edit or add to this file if you wish to upgrade Magento to newer
* versions in the future. If you wish to customize Magento for your
* needs please refer to http://www.magento.com for more information.
*
* @category Varien
* @package Varien_File
* @copyright Copyright (c) 2006-2015 X.commerce, Inc. (http://www.magento.com)
* @license http://opensource.org/licenses/osl-3.0.php Open Software License (OSL 3.0)
*/
/**
* File upload class
*
* ATTENTION! This class must be used like abstract class and must added
* validation by protected file extension list to extended class
*
* @category Varien
* @package Varien_File
* @author Magento Core Team <core@magentocommerce.com>
*/
class Varien_File_Uploader
{
/**
* Uploaded file handle (copy of $_FILES[] element)
*
* @var array
* @access protected
*/
protected $_file;
/**
* Uploaded file mime type
*
* @var string
* @access protected
*/
protected $_fileMimeType;
/**
* Upload type. Used to right handle $_FILES array.
*
* @var Varien_File_Uploader::SINGLE_STYLE|Varien_File_Uploader::MULTIPLE_STYLE
* @access protected
*/
protected $_uploadType;
/**
* The name of uploaded file. By default it is original file name, but when
* we will change file name, this variable will be changed too.
*
* @var string
* @access protected
*/
protected $_uploadedFileName;
/**
* The name of destination directory
*
* @var string
* @access protected
*/
protected $_uploadedFileDir;
/**
* If this variable is set to TRUE, our library will be able to automaticaly create
* non-existant directories.
*
* @var bool
* @access protected
*/
protected $_allowCreateFolders = true;
/**
* If this variable is set to TRUE, uploaded file name will be changed if some file with the same
* name already exists in the destination directory (if enabled).
*
* @var bool
* @access protected
*/
protected $_allowRenameFiles = false;
/**
* If this variable is set to TRUE, files despersion will be supported.
*
* @var bool
* @access protected
*/
protected $_enableFilesDispersion = false;
/**
* This variable is used both with $_enableFilesDispersion == true
* It helps to avoid problems after migrating from case-insensitive file system to case-insensitive
* (e.g. NTFS->ext or ext->NTFS)
*
* @var bool
* @access protected
*/
protected $_caseInsensitiveFilenames = true;
/**
* @var string
* @access protected
*/
protected $_dispretionPath = null;
protected $_fileExists = false;
protected $_allowedExtensions = null;
/**
* List of valid MIME-Types.
*
* @var array
*/
protected $_validMimeTypes = array();
/**
* Validate callbacks storage
*
* @var array
* @access protected
*/
protected $_validateCallbacks = array();
const SINGLE_STYLE = 0;
const MULTIPLE_STYLE = 1;
const TMP_NAME_EMPTY = 666;
/**
* Resulting of uploaded file
*
* @var array|bool Array with file info keys: path, file. Result is
* FALSE when file not uploaded
*/
protected $_result;
function __construct($fileId)
{
$this->_setUploadFileId($fileId);
if(!file_exists($this->_file['tmp_name'])) {
$code = empty($this->_file['tmp_name']) ? self::TMP_NAME_EMPTY : 0;
throw new Exception('File was not uploaded.', $code);
} else {
$this->_fileExists = true;
}
}
/**
* After save logic
*
* @param array $result
* @return Varien_File_Uploader
*/
protected function _afterSave($result)
{
return $this;
}
/**
* Used to save uploaded file into destination folder with
* original or new file name (if specified)
*
* @param string $destinationFolder
* @param string $newFileName
* @access public
* @return void|bool
*/
public function save($destinationFolder, $newFileName = null)
{
$this->_validateFile();
if ($this->_allowCreateFolders) {
$this->_createDestinationFolder($destinationFolder);
}
if (!is_writable($destinationFolder)) {
throw new Exception('Destination folder is not writable or does not exists.');
}
$this->_result = false;
$destinationFile = $destinationFolder;
$fileName = isset($newFileName) ? $newFileName : $this->_file['name'];
$fileName = self::getCorrectFileName($fileName);
if ($this->_enableFilesDispersion) {
$fileName = $this->correctFileNameCase($fileName);
$this->setAllowCreateFolders(true);
$this->_dispretionPath = self::getDispretionPath($fileName);
$destinationFile.= $this->_dispretionPath;
$this->_createDestinationFolder($destinationFile);
}
if ($this->_allowRenameFiles) {
$fileName = self::getNewFileName(self::_addDirSeparator($destinationFile) . $fileName);
}
$destinationFile = self::_addDirSeparator($destinationFile) . $fileName;
$this->_result = $this->_moveFile($this->_file['tmp_name'], $destinationFile);
if ($this->_result) {
/** Fix - Changed Permissions - chmod($destinationFile, 0640); */
chmod($destinationFile, 0644);
if ($this->_enableFilesDispersion) {
$fileName = str_replace(DIRECTORY_SEPARATOR, '/',
self::_addDirSeparator($this->_dispretionPath)) . $fileName;
}
$this->_uploadedFileName = $fileName;
$this->_uploadedFileDir = $destinationFolder;
$this->_result = $this->_file;
$this->_result['path'] = $destinationFolder;
$this->_result['file'] = $fileName;
$this->_afterSave($this->_result);
}
return $this->_result;
}
/**
* Move files from TMP folder into destination folder
*
* @param string $tmpPath
* @param string $destPath
* @return bool
*/
protected function _moveFile($tmpPath, $destPath)
{
return move_uploaded_file($tmpPath, $destPath);
}
/**
* Validate file before save
*
* @access public
*/
protected function _validateFile()
{
if ($this->_fileExists === false) {
return;
}
//is file extension allowed
if (!$this->checkAllowedExtension($this->getFileExtension())) {
throw new Exception('Disallowed file type.');
}
/*
* Validate MIME-Types.
*/
if (!$this->checkMimeType($this->_validMimeTypes)) {
throw new Exception('Invalid MIME type.');
}
//run validate callbacks
foreach ($this->_validateCallbacks as $params) {
if (is_object($params['object']) && method_exists($params['object'], $params['method'])) {
$params['object']->$params['method']($this->_file['tmp_name']);
}
}
}
/**
* Returns extension of the uploaded file
*
* @return string
*/
public function getFileExtension()
{
return $this->_fileExists ? pathinfo($this->_file['name'], PATHINFO_EXTENSION) : '';
}
/**
* Add validation callback model for us in self::_validateFile()
*
* @param string $callbackName
* @param object $callbackObject
* @param string $callbackMethod Method name of $callbackObject. It must
* have interface (string $tmpFilePath)
* @return Varien_File_Uploader
*/
public function addValidateCallback($callbackName, $callbackObject, $callbackMethod)
{
$this->_validateCallbacks[$callbackName] = array(
'object' => $callbackObject,
'method' => $callbackMethod
);
return $this;
}
/**
* Delete validation callback model for us in self::_validateFile()
*
* @param string $callbackName
* @access public
* @return Varien_File_Uploader
*/
public function removeValidateCallback($callbackName)
{
if (isset($this->_validateCallbacks[$callbackName])) {
unset($this->_validateCallbacks[$callbackName]);
}
return $this;
}
/**
* Correct filename with special chars and spaces
*
* @param string $fileName
* @return string
*/
static public function getCorrectFileName($fileName)
{
$fileName = preg_replace('/[^a-z0-9_\\-\\.]+/i', '_', $fileName);
$fileInfo = pathinfo($fileName);
if (preg_match('/^_+$/', $fileInfo['filename'])) {
$fileName = 'file.' . $fileInfo['extension'];
}
return $fileName;
}
/**
* Convert filename to lowercase in case of case-insensitive file names
*
* @param string
* @return string
*/
public function correctFileNameCase($fileName)
{
if ($this->_caseInsensitiveFilenames) {
return strtolower($fileName);
}
return $fileName;
}
static protected function _addDirSeparator($dir)
{
if (substr($dir,-1) != DIRECTORY_SEPARATOR) {
$dir.= DIRECTORY_SEPARATOR;
}
return $dir;
}
/**
* Used to check if uploaded file mime type is valid or not
*
* @param array $validTypes
* @access public
* @return bool
*/
public function checkMimeType($validTypes = array())
{
try {
if (count($validTypes) > 0) {
$validator = new Zend_Validate_File_MimeType($validTypes);
return $validator->isValid($this->_file['tmp_name']);
}
return true;
} catch (Exception $e) {
return false;
}
}
/**
* Returns a name of uploaded file
*
* @access public
* @return string
*/
public function getUploadedFileName()
{
return $this->_uploadedFileName;
}
/**
* Used to set {@link _allowCreateFolders} value
*
* @param mixed $flag
* @access public
* @return Varien_File_Uploader
*/
public function setAllowCreateFolders($flag)
{
$this->_allowCreateFolders = $flag;
return $this;
}
/**
* Used to set {@link _allowRenameFiles} value
*
* @param mixed $flag
* @access public
* @return Varien_File_Uploader
*/
public function setAllowRenameFiles($flag)
{
$this->_allowRenameFiles = $flag;
return $this;
}
/**
* Used to set {@link _enableFilesDispersion} value
*
* @param mixed $flag
* @access public
* @return Varien_File_Uploader
*/
public function setFilesDispersion($flag)
{
$this->_enableFilesDispersion = $flag;
return $this;
}
/**
* Filenames Case-sensitivity setter
*
* @param mixed $flag
* @return Varien_File_Uploader
*/
public function setFilenamesCaseSensitivity($flag)
{
$this->_caseInsensitiveFilenames = $flag;
return $this;
}
public function setAllowedExtensions($extensions = array())
{
foreach ((array)$extensions as $extension) {
$this->_allowedExtensions[] = strtolower($extension);
}
return $this;
}
/**
* Set valid MIME-types.
*
* @param array $mimeTypes
* @return Varien_File_Uploader
*/
public function setValidMimeTypes($mimeTypes = array())
{
$this->_validMimeTypes = array();
foreach ((array) $mimeTypes as $mimeType) {
$this->_validMimeTypes[] = $mimeType;
}
return $this;
}
/**
* Check if specified extension is allowed
*
* @param string $extension
* @return boolean
*/
public function checkAllowedExtension($extension)
{
if (!is_array($this->_allowedExtensions) || empty($this->_allowedExtensions)) {
return true;
}
return in_array(strtolower($extension), $this->_allowedExtensions);
}
/**
* @deprecated after 1.5.0.0-beta2
*
* @param string $extension
* @return boolean
*/
public function chechAllowedExtension($extension)
{
return $this->checkAllowedExtension($extension);
}
private function _getMimeType()
{
return $this->_file['type'];
}
private function _setUploadFileId($fileId)
{
if (empty($_FILES)) {
throw new Exception('$_FILES array is empty', self::TMP_NAME_EMPTY);
}
if (is_array($fileId)) {
$this->_uploadType = self::MULTIPLE_STYLE;
$this->_file = $fileId;
} else {
preg_match("/^(.*?)\[(.*?)\]$/", $fileId, $file);
if (count($file) > 0 && (count($file[0]) > 0) && (count($file[1]) > 0)) {
array_shift($file);
$this->_uploadType = self::MULTIPLE_STYLE;
$fileAttributes = $_FILES[$file[0]];
$tmp_var = array();
foreach ($fileAttributes as $attributeName => $attributeValue) {
$tmp_var[$attributeName] = $attributeValue[$file[1]];
}
$fileAttributes = $tmp_var;
$this->_file = $fileAttributes;
} elseif( count($fileId) > 0 && isset($_FILES[$fileId])) {
$this->_uploadType = self::SINGLE_STYLE;
$this->_file = $_FILES[$fileId];
} elseif( $fileId == '' ) {
throw new Exception('Invalid parameter given. A valid $_FILES[] identifier is expected.');
}
}
}
private function _createDestinationFolder($destinationFolder)
{
if (!$destinationFolder) {
return $this;
}
if (substr($destinationFolder, -1) == DIRECTORY_SEPARATOR) {
$destinationFolder = substr($destinationFolder, 0, -1);
}
/** Previous - fixed Permissions - if (!(@is_dir($destinationFolder) || @mkdir($destinationFolder, 0750, true))) { */
if (!(@is_dir($destinationFolder) || @mkdir($destinationFolder, 0755, true))) {
throw new Exception("Unable to create directory '{$destinationFolder}'.");
}
return $this;
}
static public function getNewFileName($destFile)
{
$fileInfo = pathinfo($destFile);
if (file_exists($destFile)) {
$index = 1;
$baseName = $fileInfo['filename'] . '.' . $fileInfo['extension'];
while( file_exists($fileInfo['dirname'] . DIRECTORY_SEPARATOR . $baseName) ) {
$baseName = $fileInfo['filename']. '_' . $index . '.' . $fileInfo['extension'];
$index ++;
}
$destFileName = $baseName;
} else {
return $fileInfo['basename'];
}
return $destFileName;
}
static public function getDispretionPath($fileName)
{
$char = 0;
$dispretionPath = '';
while (($char < 2) && ($char < strlen($fileName))) {
if (empty($dispretionPath)) {
$dispretionPath = DIRECTORY_SEPARATOR
. ('.' == $fileName[$char] ? '_' : $fileName[$char]);
} else {
$dispretionPath = self::_addDirSeparator($dispretionPath)
. ('.' == $fileName[$char] ? '_' : $fileName[$char]);
}
$char ++;
}
return $dispretionPath;
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment