[ Index ]

PHP Cross Reference of Joomla 4.2.2 documentation

title

Body

[close]

/libraries/src/Helper/ -> MediaHelper.php (source)

   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  }


Generated: Wed Sep 7 05:41:13 2022 Chilli.vc Blog - For Webmaster,Blog-Writer,System Admin and Domainer