[ Index ] |
PHP Cross Reference of Joomla 4.2.2 documentation |
[Summary view] [Print] [Text view]
1 <?php 2 3 /** 4 * Joomla! Content Management System 5 * 6 * @copyright (C) 2013 Open Source Matters, Inc. <https://www.joomla.org> 7 * @license GNU General Public License version 2 or later; see LICENSE.txt 8 */ 9 10 namespace Joomla\CMS\Helper; 11 12 use enshrined\svgSanitize\Sanitizer; 13 use Joomla\CMS\Component\ComponentHelper; 14 use Joomla\CMS\Factory; 15 use Joomla\CMS\Filesystem\File; 16 use Joomla\CMS\Filter\InputFilter; 17 use Joomla\CMS\Language\Text; 18 use Joomla\CMS\Plugin\PluginHelper; 19 use Joomla\Registry\Registry; 20 21 // phpcs:disable PSR1.Files.SideEffects 22 \defined('JPATH_PLATFORM') or die; 23 // phpcs:enable PSR1.Files.SideEffects 24 25 /** 26 * Media helper class 27 * 28 * @since 3.2 29 */ 30 class MediaHelper 31 { 32 /** 33 * A special list of blocked executable extensions, skipping executables that are 34 * typically executable in the webserver context as those are fetched from 35 * Joomla\CMS\Filter\InputFilter 36 * 37 * @var string[] 38 * @since 4.0.0 39 */ 40 public const EXECUTABLES = array( 41 'js', 'exe', 'dll', 'go', 'ade', 'adp', 'bat', 'chm', 'cmd', 'com', 'cpl', 'hta', 42 'ins', 'isp', 'jse', 'lib', 'mde', 'msc', 'msp', 'mst', 'pif', 'scr', 'sct', 'shb', 43 'sys', 'vb', 'vbe', 'vbs', 'vxd', 'wsc', 'wsf', 'wsh', 'html', 'htm', 'msi' 44 ); 45 46 /** 47 * Checks if the file is an image 48 * 49 * @param string $fileName The filename 50 * 51 * @return boolean 52 * 53 * @since 3.2 54 */ 55 public static function isImage($fileName) 56 { 57 static $imageTypes = 'xcf|odg|gif|jpg|jpeg|png|bmp|webp'; 58 59 return preg_match("/\.(?:$imageTypes)$/i", $fileName); 60 } 61 62 /** 63 * Gets the file extension for purposed of using an icon 64 * 65 * @param string $fileName The filename 66 * 67 * @return string File extension to determine icon 68 * 69 * @since 3.2 70 */ 71 public static function getTypeIcon($fileName) 72 { 73 return strtolower(substr($fileName, strrpos($fileName, '.') + 1)); 74 } 75 76 /** 77 * Get the Mime type 78 * 79 * @param string $file The link to the file to be checked 80 * @param boolean $isImage True if the passed file is an image else false 81 * 82 * @return mixed the mime type detected false on error 83 * 84 * @since 3.7.2 85 */ 86 public static function getMimeType($file, $isImage = false) 87 { 88 // If we can't detect anything mime is false 89 $mime = false; 90 91 try { 92 if ($isImage && \function_exists('exif_imagetype')) { 93 $mime = image_type_to_mime_type(exif_imagetype($file)); 94 } elseif ($isImage && \function_exists('getimagesize')) { 95 $imagesize = getimagesize($file); 96 $mime = $imagesize['mime'] ?? false; 97 } elseif (\function_exists('mime_content_type')) { 98 // We have mime magic. 99 $mime = mime_content_type($file); 100 } elseif (\function_exists('finfo_open')) { 101 // We have fileinfo 102 $finfo = finfo_open(FILEINFO_MIME_TYPE); 103 $mime = finfo_file($finfo, $file); 104 finfo_close($finfo); 105 } 106 } catch (\Exception $e) { 107 // If we have any kind of error here => false; 108 return false; 109 } 110 111 // If we can't detect the mime try it again 112 if ($mime === 'application/octet-stream' && $isImage === true) { 113 $mime = static::getMimeType($file, false); 114 } 115 116 // We have a mime here 117 return $mime; 118 } 119 120 /** 121 * Checks the Mime type 122 * 123 * @param string $mime The mime to be checked 124 * @param string $component The optional name for the component storing the parameters 125 * 126 * @return boolean true if mime type checking is disabled or it passes the checks else false 127 * 128 * @since 3.7 129 */ 130 private function checkMimeType($mime, $component = 'com_media'): bool 131 { 132 $params = ComponentHelper::getParams($component); 133 134 if ($params->get('check_mime', 1)) { 135 $allowedMime = $params->get( 136 'upload_mime', 137 'image/jpeg,image/gif,image/png,image/bmp,image/webp,application/msword,application/excel,' . 138 'application/pdf,application/powerpoint,text/plain,application/x-zip' 139 ); 140 141 // Get the mime type configuration 142 $allowedMime = array_map('trim', explode(',', $allowedMime)); 143 144 // Mime should be available and in the allowed list 145 return !empty($mime) && \in_array($mime, $allowedMime); 146 } 147 148 // We don't check mime at all or it passes the checks 149 return true; 150 } 151 152 /** 153 * Checks the file extension 154 * 155 * @param string $extension The extension to be checked 156 * @param string $component The optional name for the component storing the parameters 157 * 158 * @return boolean true if it passes the checks else false 159 * 160 * @since 4.0.0 161 */ 162 public static function checkFileExtension($extension, $component = 'com_media', $allowedExecutables = array()): bool 163 { 164 $params = ComponentHelper::getParams($component); 165 166 // Media file names should never have executable extensions buried in them. 167 $executables = array_merge(self::EXECUTABLES, InputFilter::FORBIDDEN_FILE_EXTENSIONS); 168 169 // Remove allowed executables from array 170 if (count($allowedExecutables)) { 171 $executables = array_diff($executables, $allowedExecutables); 172 } 173 174 if (in_array($extension, $executables, true)) { 175 return false; 176 } 177 178 $allowable = array_map('trim', explode(',', $params->get('restrict_uploads_extensions', 'bmp,gif,jpg,jpeg,png,webp,ico,mp3,m4a,mp4a,ogg,mp4,mp4v,mpeg,mov,odg,odp,ods,odt,pdf,ppt,txt,xcf,xls,csv'))); 179 $ignored = array_map('trim', explode(',', $params->get('ignore_extensions', ''))); 180 181 if ($extension == '' || $extension == false || (!\in_array($extension, $allowable, true) && !\in_array($extension, $ignored, true))) { 182 return false; 183 } 184 185 // We don't check mime at all or it passes the checks 186 return true; 187 } 188 189 /** 190 * Checks if the file can be uploaded 191 * 192 * @param array $file File information 193 * @param string $component The option name for the component storing the parameters 194 * @param string $allowedExecutables Array of executable file types that shall be whitelisted 195 * 196 * @return boolean 197 * 198 * @since 3.2 199 */ 200 public function canUpload($file, $component = 'com_media', $allowedExecutables = array()) 201 { 202 $app = Factory::getApplication(); 203 $params = ComponentHelper::getParams($component); 204 205 if (empty($file['name'])) { 206 $app->enqueueMessage(Text::_('JLIB_MEDIA_ERROR_UPLOAD_INPUT'), 'error'); 207 208 return false; 209 } 210 211 if ($file['name'] !== File::makeSafe($file['name'])) { 212 $app->enqueueMessage(Text::_('JLIB_MEDIA_ERROR_WARNFILENAME'), 'error'); 213 214 return false; 215 } 216 217 $filetypes = explode('.', $file['name']); 218 219 if (\count($filetypes) < 2) { 220 // There seems to be no extension 221 $app->enqueueMessage(Text::_('JLIB_MEDIA_ERROR_WARNFILETYPE'), 'error'); 222 223 return false; 224 } 225 226 array_shift($filetypes); 227 228 // Media file names should never have executable extensions buried in them. 229 $executables = array_merge(self::EXECUTABLES, InputFilter::FORBIDDEN_FILE_EXTENSIONS); 230 231 // Remove allowed executables from array 232 if (count($allowedExecutables)) { 233 $executables = array_diff($executables, $allowedExecutables); 234 } 235 236 $check = array_intersect($filetypes, $executables); 237 238 if (!empty($check)) { 239 $app->enqueueMessage(Text::_('JLIB_MEDIA_ERROR_WARNFILETYPE'), 'error'); 240 241 return false; 242 } 243 244 $filetype = array_pop($filetypes); 245 246 $allowable = array_map('trim', explode(',', $params->get('restrict_uploads_extensions', 'bmp,gif,jpg,jpeg,png,webp,ico,mp3,m4a,mp4a,ogg,mp4,mp4v,mpeg,mov,odg,odp,ods,odt,pdf,png,ppt,txt,xcf,xls,csv'))); 247 $ignored = array_map('trim', explode(',', $params->get('ignore_extensions', ''))); 248 249 if ($filetype == '' || $filetype == false || (!\in_array($filetype, $allowable) && !\in_array($filetype, $ignored))) { 250 $app->enqueueMessage(Text::_('JLIB_MEDIA_ERROR_WARNFILETYPE'), 'error'); 251 252 return false; 253 } 254 255 $maxSize = (int) ($params->get('upload_maxsize', 0) * 1024 * 1024); 256 257 if ($maxSize > 0 && (int) $file['size'] > $maxSize) { 258 $app->enqueueMessage(Text::_('JLIB_MEDIA_ERROR_WARNFILETOOLARGE'), 'error'); 259 260 return false; 261 } 262 263 if ($params->get('restrict_uploads', 1)) { 264 $allowedExtensions = array_map('trim', explode(',', $params->get('restrict_uploads_extensions', 'bmp,gif,jpg,jpeg,png,webp,ico,mp3,m4a,mp4a,ogg,mp4,mp4v,mpeg,mov,odg,odp,ods,odt,pdf,png,ppt,txt,xcf,xls,csv'))); 265 266 if (\in_array($filetype, $allowedExtensions)) { 267 // If tmp_name is empty, then the file was bigger than the PHP limit 268 if (!empty($file['tmp_name'])) { 269 // Get the mime type this is an image file 270 $mime = static::getMimeType($file['tmp_name'], true); 271 272 // Did we get anything useful? 273 if ($mime != false) { 274 $result = $this->checkMimeType($mime, $component); 275 276 // If the mime type is not allowed we don't upload it and show the mime code error to the user 277 if ($result === false) { 278 $app->enqueueMessage(Text::sprintf('JLIB_MEDIA_ERROR_WARNINVALID_MIMETYPE', $mime), 'error'); 279 280 return false; 281 } 282 } else { 283 // We can't detect the mime type so it looks like an invalid image 284 $app->enqueueMessage(Text::_('JLIB_MEDIA_ERROR_WARNINVALID_IMG'), 'error'); 285 286 return false; 287 } 288 } else { 289 $app->enqueueMessage(Text::_('JLIB_MEDIA_ERROR_WARNFILETOOLARGE'), 'error'); 290 291 return false; 292 } 293 } elseif (!\in_array($filetype, $ignored)) { 294 // Get the mime type this is not an image file 295 $mime = static::getMimeType($file['tmp_name'], false); 296 297 // Did we get anything useful? 298 if ($mime != false) { 299 $result = $this->checkMimeType($mime, $component); 300 301 // If the mime type is not allowed we don't upload it and show the mime code error to the user 302 if ($result === false) { 303 $app->enqueueMessage(Text::sprintf('JLIB_MEDIA_ERROR_WARNINVALID_MIMETYPE', $mime), 'error'); 304 305 return false; 306 } 307 } else { 308 // We can't detect the mime type so it looks like an invalid file 309 $app->enqueueMessage(Text::_('JLIB_MEDIA_ERROR_WARNINVALID_MIME'), 'error'); 310 311 return false; 312 } 313 314 if (!Factory::getUser()->authorise('core.manage', $component)) { 315 $app->enqueueMessage(Text::_('JLIB_MEDIA_ERROR_WARNNOTADMIN'), 'error'); 316 317 return false; 318 } 319 } 320 } 321 322 if ($filetype === 'svg') { 323 $sanitizer = new Sanitizer(); 324 325 $isValid = $sanitizer->sanitize(file_get_contents($file['tmp_name'])); 326 327 $svgErrors = $sanitizer->getXmlIssues(); 328 329 // We allow comments 330 foreach ($svgErrors as $i => $error) { 331 if ($error['message'] === 'Suspicious node \'#comment\'') { 332 unset($svgErrors[$i]); 333 } 334 } 335 336 if ($isValid === false || count($svgErrors)) { 337 $app->enqueueMessage(Text::_('JLIB_MEDIA_ERROR_WARNIEXSS'), 'error'); 338 339 return false; 340 } 341 } 342 343 return true; 344 } 345 346 /** 347 * Calculate the size of a resized image 348 * 349 * @param integer $width Image width 350 * @param integer $height Image height 351 * @param integer $target Target size 352 * 353 * @return array The new width and height 354 * 355 * @since 3.2 356 */ 357 public static function imageResize($width, $height, $target) 358 { 359 /* 360 * Takes the larger size of the width and height and applies the 361 * formula accordingly. This is so this script will work 362 * dynamically with any size image 363 */ 364 if ($width > $height) { 365 $percentage = ($target / $width); 366 } else { 367 $percentage = ($target / $height); 368 } 369 370 // Gets the new value and applies the percentage, then rounds the value 371 $width = round($width * $percentage); 372 $height = round($height * $percentage); 373 374 return array($width, $height); 375 } 376 377 /** 378 * Counts the files and directories in a directory that are not php or html files. 379 * 380 * @param string $dir Directory name 381 * 382 * @return array The number of media files and directories in the given directory 383 * 384 * @since 3.2 385 */ 386 public function countFiles($dir) 387 { 388 $total_file = 0; 389 $total_dir = 0; 390 391 if (is_dir($dir)) { 392 $d = dir($dir); 393 394 while (($entry = $d->read()) !== false) { 395 if ($entry[0] !== '.' && strpos($entry, '.html') === false && strpos($entry, '.php') === false && is_file($dir . DIRECTORY_SEPARATOR . $entry)) { 396 $total_file++; 397 } 398 399 if ($entry[0] !== '.' && is_dir($dir . DIRECTORY_SEPARATOR . $entry)) { 400 $total_dir++; 401 } 402 } 403 404 $d->close(); 405 } 406 407 return array($total_file, $total_dir); 408 } 409 410 /** 411 * Small helper function that properly converts any 412 * configuration options to their byte representation. 413 * 414 * @param string|integer $val The value to be converted to bytes. 415 * 416 * @return integer The calculated bytes value from the input. 417 * 418 * @since 3.3 419 */ 420 public function toBytes($val) 421 { 422 switch ($val[\strlen($val) - 1]) { 423 case 'M': 424 case 'm': 425 return (int) $val * 1048576; 426 case 'K': 427 case 'k': 428 return (int) $val * 1024; 429 case 'G': 430 case 'g': 431 return (int) $val * 1073741824; 432 default: 433 return $val; 434 } 435 } 436 437 /** 438 * Method to check if the given directory is a directory configured in FileSystem - Local plugin 439 * 440 * @param string $directory 441 * 442 * @return boolean 443 * 444 * @since 4.0.0 445 */ 446 public static function isValidLocalDirectory($directory) 447 { 448 $plugin = PluginHelper::getPlugin('filesystem', 'local'); 449 450 if ($plugin) { 451 $params = new Registry($plugin->params); 452 453 $directories = $params->get('directories', '[{"directory": "images"}]'); 454 455 // Do a check if default settings are not saved by user 456 // If not initialize them manually 457 if (is_string($directories)) { 458 $directories = json_decode($directories); 459 } 460 461 foreach ($directories as $directoryEntity) { 462 if ($directoryEntity->directory === $directory) { 463 return true; 464 } 465 } 466 } 467 468 return false; 469 } 470 471 /** 472 * Helper method get clean data for value stores in a Media form field by removing adapter information 473 * from the value if available (in this case, the value will have this format: 474 * images/headers/blue-flower.jpg#joomlaImage://local-images/headers/blue-flower.jpg?width=700&height=180) 475 * 476 * @param string $value 477 * 478 * @return string 479 * 480 * @since 4.0.0 481 */ 482 public static function getCleanMediaFieldValue($value) 483 { 484 if ($pos = strpos($value, '#')) { 485 return substr($value, 0, $pos); 486 } 487 488 return $value; 489 } 490 }
title
Description
Body
title
Description
Body
title
Description
Body
title
Body
Generated: Wed Sep 7 05:41:13 2022 | Chilli.vc Blog - For Webmaster,Blog-Writer,System Admin and Domainer |