Skip to content

Instantly share code, notes, and snippets.

@iamrobert
Last active August 26, 2021 13:01
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 iamrobert/587be06632be683f865d57f1f2291742 to your computer and use it in GitHub Desktop.
Save iamrobert/587be06632be683f865d57f1f2291742 to your computer and use it in GitHub Desktop.
FLEXIContent Upload FIle - handle Chinese/remove spaces
<?php
//components/com_flexicontent/classes/helpers/upload.php
//Handles Chinese
defined( '_JEXEC' ) or die( 'Restricted access' );
class flexicontent_upload
{
static function makeSafe($file, $language = null)
{
// Replace [] with () and multiple space with single, but firest remove any leading / trailing spaces
$file = trim($file);
$file = str_replace('[', '(', $file);
$file = str_replace(']', ')', $file);
$file = preg_replace('![\s]+!', '-', $file);
// Replace $*"[]:;|/ with dash after removing any leading / trailing spaces
$file = preg_replace('![\$\*\"\[\]\:\;\|\/]]+!', '_', $file);
//CHINESE FIX
if (preg_match('/[\x{4e00}-\x{9fa5}]+.*/u', $file) === 1) {
$file = rand(0,100000000);
}
// Remove any trailing dots, as those aren't ever valid file names
$file = rtrim($file, '.');
// Regex for replacing non safe characters
$regex = array('#(\.){2,}#', '#[^A-Za-z0-9\(\)\.\_\-]#', '#^\.#');
// Language transliteration should include given language, and also site + admin defaults (most useful is site default)
$lang_params = JComponentHelper::getParams('com_languages');
$lang_site_default = $lang_params->get('site', '*');
$lang_admin_default = $lang_params->get('admin', '*');
$langs[$language] = $language && $language != '*';
$langs[$lang_site_default] = $lang_site_default != '*';
$langs[$lang_admin_default] = $lang_admin_default != '*';
// Try to transliterate according to given language and site + admin default languages
$file_safe = false;
$transformed = '';
foreach($langs as $language => $do)
{
if ($do)
{
$transformed = JLanguage::getInstance($language)->transliterate($file);
$file_safe = $transformed ? preg_replace($regex, '', $transformed) : false;
// Stop trying transliterations if a complete job was done
if ($transformed && $transformed == $file_safe)
{
break;
}
$file_safe = false;
}
}
// Finally if none of transliterations did a good enough job (if less than 50% of file remained)
// (It could be because of wrong language(s) tried)
// then avoid bad looking filenames by using current time
if ( !$file_safe )
{
if (strlen($transformed) < 0.5 * strlen($file))
{
$ext = self::getExt($file);
$file_safe = date('Y-m-d_H.i.s') .'.'. $ext;
}
else
{
$file_safe = $transformed;
}
}
// Return filename that is filesystem safe
return $file_safe;
}
static function parseByteLimit($limit)
{
if (is_numeric($limit)) return $limit; // already in bytes
$v = (int)$limit;
$type = substr($limit, -1);
switch (strtoupper($type)) {
case 'P': $v *= 1024;
case 'T': $v *= 1024;
case 'G': $v *= 1024;
case 'M': $v *= 1024;
case 'K': $v *= 1024;
break;
}
return $v;
}
/**
* Gets upload Limits
*
* @return array with limits
* @since 3.0
*/
static function getPHPuploadLimit()
{
$post_max = flexicontent_upload::parseByteLimit(ini_get('post_max_size'));
$upload_max = flexicontent_upload::parseByteLimit(ini_get('upload_max_filesize'));
if ($upload_max < $post_max) {
$limit = array('value'=>$upload_max, 'name'=>'upload_max_filesize');
}
else {
$limit = array('value'=>$post_max, 'name'=>'post_max_size');
}
// Sucosin limitation
if (extension_loaded('suhosin')) {
$post_max = flexicontent_upload::parseByteLimit(ini_get('suhosin.post.max_value_length'));
if ($post_max < $limit['value']) $limit = array('value'=>$post_max, 'name'=>'suhosin.post.max_value_length');
}
return $limit;
}
/**
* Gets the extension of a file name
*
* @param string $file The file name
* @return string | boolean The file extension or false if file does not have extension
* @since 1.5
*/
static function getExt($file)
{
$dot = strrpos($file, '.');
if ($dot === false)
{
return '';
}
return (string) substr($file, $dot + 1);
//return pathinfo($file, PATHINFO_EXTENSION);
}
/**
* Checks uploaded file
*
* @param string $file The file name
* @param string $err Set (return) the error string in it
* @param string $file view 's parameters
* @return string The file extension
* @since 1.5
*/
static function check(&$file, &$err, &$params, $language=null)
{
if (!$params)
{
$params = JComponentHelper::getParams( 'com_flexicontent' );
}
// ************************
// Check non-empty filename
// ************************
if(empty($file['name']))
{
$err = 'FLEXI_PLEASE_INPUT_A_FILE';
return false;
}
jimport('joomla.filesystem.file');
// ***************************************************************
// Make filename safe, transliterating according to given language
// ***************************************************************
$language = $language ? $language : (!empty($file['language']) ? $file['language'] : '*'); // * would usually be interpretted as frontend site default language
$file['name'] = flexicontent_upload::makeSafe($file['name'], $language);
// ***************************************
// Check if the image file type is allowed
// ***************************************
$format = strtolower(flexicontent_upload::getExt($file['name']));
$allowed_exts = preg_split("/[\s]*,[\s]*/", strtolower($params->get('upload_extensions', 'bmp,csv,doc,docx,gif,ico,jpg,jpeg,odg,odp,ods,odt,pdf,png,ppt,pptx,txt,xcf,xls,xlsx,zip,ics')));
$allowed_exts = array_flip($allowed_exts);
$ignored_exts = preg_split("/[\s]*,[\s]*/", strtolower($params->get('ignore_extensions')));
$ignored_exts = array_flip($ignored_exts);
if (!isset($allowed_exts[$format]) && !isset($ignored_exts[$format]))
{
$err = 'FLEXI_WARNFILETYPE';
return false;
}
// **************
// Check filesize
// **************
$maxSize = (int) $params->get( 'upload_maxsize', 0 );
if ($maxSize > 0 && (int) $file['size'] > $maxSize)
{
$err = 'FLEXI_WARNFILETOOLARGE';
return false;
}
// **********************************************
// Check extension and mime type are both allowed
// **********************************************
$imginfo = null;
$images = explode( ',', $params->get( 'image_extensions' ));
if ($params->get('restrict_uploads', 1) )
{
if (in_array($format, $images)) // if its an image run it through getimagesize
{
if (($imginfo = getimagesize($file['tmp_name'])) === FALSE)
{
$err = 'FLEXI_WARNINVALIDIMG';
return false;
}
}
else if ( !isset($ignored_exts[$format]) )
{
// if its not an image...and we're not ignoring it
$allowed_mime = explode(',', $params->get('upload_mime'));
$illegal_mime = explode(',', $params->get('upload_mime_illegal'));
if (function_exists('finfo_open') /*&& $params->get('check_mime',1)*/)
{
// We have fileinfo
$finfo = finfo_open(FILEINFO_MIME);
$type = finfo_file($finfo, $file['tmp_name']);
if(strlen($type) && !in_array($type, $allowed_mime) && in_array($type, $illegal_mime)) {
$err = 'FLEXI_WARNINVALIDMIME';
return false;
}
finfo_close($finfo);
}
else if(function_exists('mime_content_type') /*&& $params->get('check_mime',1)*/)
{
// we have mime magic
$type = mime_content_type($file['tmp_name']);
if(strlen($type) && !in_array($type, $allowed_mime) && in_array($type, $illegal_mime)) {
$err = 'FLEXI_WARNINVALIDMIME';
return false;
}
}
}
}
// ***************************
// Check for XSS safe contents
// ***************************
$xss_check = JFile::read($file['tmp_name'], false, 256);
$html_tags = array('abbr','acronym','address','applet','area','audioscope','base','basefont',
'bdo','bgsound','big','blackface','blink','blockquote','body','bq','br','button','caption',
'center','cite','code','col','colgroup','comment','custom','dd','del','dfn','dir','div','dl','dt',
'em','embed','fieldset','fn','font','form','frame','frameset','h1','h2','h3','h4','h5','h6','head',
'hr','html','iframe','ilayer','img','input','ins','isindex','keygen','kbd','label','layer','legend',
'li','limittext','link','listing','map','marquee','menu','meta','multicol','nobr','noembed','noframes',
'noscript','nosmartquotes','object','ol','optgroup','option','param','plaintext','pre','rt','ruby','s','samp',
'script','select','server','shadow','sidebar','small','spacer','span','strike','strong','style','sub','sup','table',
'tbody','td','textarea','tfoot','th','thead','title','tr','tt','ul','var','wbr','xml','xmp','!DOCTYPE', '!--');
foreach($html_tags as $tag)
{
// A tag is '<tagname ', so we need to add < and a space or '<tagname>'
if(stristr($xss_check, '<'.$tag.' ') || stristr($xss_check, '<'.$tag.'>'))
{
$err = 'FLEXI_WARNIEXSS';
return false;
}
}
return true;
}
/**
* Sanitize the file name allowing only filesystem-safe characters, and return an unique filename for the given folder
*
* @since 1.0
*
* @param string $base_Dir the target directory
* @param string $filename the unsanitized imagefile name
*
* @return string $filename the sanitized and unique file name
*/
static function sanitize($base_Dir, $filename)
{
jimport('joomla.filesystem.file');
// Check for any trailing dots and remove them (trailing shouldn't be possible cause of the getExt check)
$filename = rtrim($filename, '.');
// Replace invalid characters with dash, if makeSafe has been called then this has already been done
$regex = array('#(\.){2,}#', '#[^A-Za-z0-9\.\_\-]#', '#^\.#');
$filename = trim(preg_replace($regex, '-', $filename));
// Get name part and extension part from the file name
$lastdotpos = strrpos( $filename, '.' );
$name = substr( $filename, 0, $lastdotpos );
$ext = substr( $filename, $lastdotpos + 1 );
// Make a unique filename by checking if it is already taken, if already taken keep incrementing counter till finding a new name
if (JFile::exists( $base_Dir . $name . '.' . $ext ))
{
$unique_num = 1;
while( JFile::exists( $base_Dir . $name . '-' . $unique_num . '.' . $ext ) )
{
$unique_num++;
}
// Create new filename out of the name and ext parts adding the unique number to it
$filename = $name . '-' . $unique_num . '.' . $ext;
}
return $filename;
}
/**
* Sanitize folders and return an unique string
*
* @since 1.5
*
* @param string $base_Dir the target directory
* @param string $foler the unsanitized folder name
*
* @return string $foldername the sanitized and unique file name
*/
static function sanitizedir($base_Dir, $folder)
{
jimport('joomla.filesystem.folder');
// Replace invalid characters with dash, if makeSafe has been called then this has already been done
$regex = array('#(\.){2,}#', '#[^A-Za-z0-9\.\_\-]#', '#^\.#');
$folder = trim(preg_replace($regex, '-', $folder));
$foldername = $folder;
//make a unique folder name for the image and check it is not already taken
if (JFolder::exists( $base_Dir . $folder ))
{
$unique_num = 1;
while( JFolder::exists( $base_Dir . $folder . '-' . $unique_num ))
{
$unique_num++;
}
// Create new folder name appending the unique number to it
$foldername = $folder . '-' . $unique_num;
}
return $foldername;
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment